222100224_林然鹏_202302软件工程实践(个人)

随笔2个月前发布 子君
36 0 0

文章目录

一、寒假作业–准备篇.技术路线1.准备工作1.1 使用git上传文件1.2 仓库地址
2.回首过去2.1 当初你为什么选择软件工程这个专业?2.2 当初对软件工程这个专业的期待和想象是什么?2.3 当初希望自己是如何投入这个专业的学习的?曾经做过什么准备,或者立下过什么FLAG吗?
3.立足当下4.展望未来4.1阅读《构建之法》,并阅读构建之法社区中前人提出的问题,选择5个及以上的问题写下自己的思考。4.2你未来的职业规划是什么?4.3 对于软件工程实践课程,你有什么理解和期望?
5.思维导图和学习路线5.1思维导图5.2路线规划图

二、个 人 实 战0.Gitcode项目地址1.PSP表格2.解题思路描述2.1 问题1 JSON数据解析,查找players信息2.2 问题2 JSON解析比赛项目,查找比赛结果
3.接口设计和实现过程3.1 接口Core的设计3.2 代码组织及接口实现过程
4.关键代码展示4.1 输出players信息4.2 输出比赛结果
5.性能改进6.单元测试6.1 DWASearch类测试6.2 Core运算核心接口测试
7.异常处理8.心得体会
三、结对作业 1–原型设计1.网页原型2.需求分析(NABCD模型)3.模型设计3.1原型开发工具3.2页面原型设计过程
4.结果汇报4.1页面设计成果4.2遇到的困难及解决方法4.3收获心得
5.效能分析和PSP表格5.1任务分配与合作过程5.2队友评价5.3PSP表格5.4效能分析

四、结对作业 2–编程实现一、 Gitcode地址二、PSP表格三、项目访问链接四、项目展示4.1 首页4.2 每日赛事4.3 详细赛况4.4 选手信息4.5 奖牌榜4.6 了解更多
五、结对讨论描述六、实现过程&功能结构图七、关键代码展示7.1前端关键代码1.请求事件2.页面代码
7.2后端关键代码1封装文件操作代码1.1文件提取:1.2将json文件转为字符串:
2.网页接口编写3.返回数据格式封装

八、总结收获&评价队友后端开发(222100225_林璞):8.1.1后端开发总结收获8.1.2对队友的评价
前端开发(222200224_林然鹏):8.2.1前端开发总结收获8.2.2对队友的评价

五、软件评测1. 调研与测评1.1 文心一言1.1.1 产品使用体验1.1.1.1 基本功能介绍和使用1.1.1.2 优缺点分析1.1.1.3 改进意见
1.1.2 Bug描述1.1.2.1 Bug发生时的测试环境1.1.2.2 Bug的可复现性及具体复现步骤1.1.2.3 Bug具体情况描述1.1.2.4 Bug分析
1.1.3 用户采访1.1.4 结论1.1.4.1 定性结论1.1.4.2 定量结论

1.2 通义听悟(与“通义千问”同源)1.2.1 产品使用体验1.2.1.1 基本功能介绍和使用1.2.1.2 优缺点分析1.2.1.3 改进意见
1.2.2 Bug1描述1.2.2.1 Bug1发生时的测试环境1.2.2.2 Bug1的可复现性及具体复现步骤1.2.2.3 Bug1具体情况描述1.2.2.4 Bug1分析
1.2.3 Bug2描述1.2.3.1 Bug2发生时的测试环境1.2.3.2 Bug2的可复现性及具体复现步骤1.2.3.3 Bug2具体情况描述1.2.3.4 Bug2分析
1.2.4 结论1.2.4.1 定性结论1.2.4.2 定量结论

2. 分析2.1文心一言分析2.1.1 同类产品对比与排名2.1.2 软件工程方面的建议2.1.3 Bug存在的原因分析2.1.4 产品开发时间预估
2.2 通义听悟分析2.2.1 同类产品对比与排名2.2.2 软件工程方面的建议2.2.3 Bug存在的原因分析2.2.4 产品开发时间预估

3. 建议和规划3.1 文心一言3.1.1 产品规划3.1.1.1 功能以及NABCD分析3.1.1.2 角色配置3.1.1.3 16周的详细计划

3.2 通义听悟3.2.1 产品规划3.2.1.1 功能以及NABCD分析3.2.1.2 角色配置3.2.1.3 16周的详细计划

3.3 市场分析3.3.1 市场概况3.3.2 市场现状

六、软工实践总结 & 个人技术博客一、五个阶段中的收获二、个人项目/结对编程/团队项目的理解与心得三、课程目标的掌握程度四、五个问题再次思考五、个人技术博客一、技术概述二、技术详述三、技术使用中遇到的问题和解决过程四、总结五、参考文献

一、寒假作业–准备篇.技术路线

这个作业属于哪个课程 福州大学-202302软件工程实践
这个作业要求在哪里 软件工程寒假实践作业
这个作业的目标 CSDN加入班级社区、学习markdown、使用gitcode、阅读《构建之法》、撰写博客、立足当下、展望未来
其他参考文献 markdown教程、Git学习、Xmind教程

1.准备工作

1.1 使用git上传文件

222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

1.2 仓库地址

222100224

2.回首过去

2.1 当初你为什么选择软件工程这个专业?

当初选择软件工程是因为这在大学里属于一个较为热门的专业,相对于部分冷门专业更加方便就业。

2.2 当初对软件工程这个专业的期待和想象是什么?

对专业的期待,软件工程顾名思义即使软件的开发与维护,期待成为一名合格的程序员,掌握编码开发能力,毕业后就业于较好的环境,稳扎稳打靠手艺赚钱。
222100224_林然鹏_202302软件工程实践(个人)

2.3 当初希望自己是如何投入这个专业的学习的?曾经做过什么准备,或者立下过什么FLAG吗?

曾以为及时完成校内课程内容,学习开发技术,毕业后就业不是问题,在几年的学习过程中以及对就业情况的了解,才明白需要花更多的时间自主学习各种知识。
曾经在一些培训机构了解过目前计算机市场就业情况,明确方向。

3.立足当下

个人信息
222100224_林然鹏_202302软件工程实践(个人) 姓名 林然鹏
出生年月 2002年10月
兴趣爱好 听歌、散步
邮箱 3030026798@qq.com
当前值
成果和获奖经历 福州大学2023-2024学年第一学期精神文明建设奖
专业水平 掌握的编程语言 JAVA、C、C++、C#、HTML、PHP…
技术框架 Yii2.0、ArkUI
开发软件 IntelliJ IDEA、Visual Studio Code、Visual Studio、DevEco Studio
累计代码量 10k+
项目经历 个人博客系统 个人博客系统,包括前台文章页面及后台管理页面。使用的技术包括Yii2框架,PHP语言,MySQL数据库技术,HTML、CSS、JavaScript技术等等。实现了前台文章、评论的增删改查功能以及后台各项管理的增删改查功能。
女鞋销售系统 使用C#语言,结合数据库技术以及GUI可视化编程实现的简单销售系统。

4.展望未来

4.1阅读《构建之法》,并阅读构建之法社区中前人提出的问题,选择5个及以上的问题写下自己的思考。

Q1.我都是大学生了,上课还要认真听老师讲课么?

答:个人认为听课与否关键取决于自身对该课程的需求以及该课程的实际意义有多大;在大学里,众所周知,存在不少水课,部分水课课上稍微做一些自主的学习并不过分,但需要尊重老师。当然,与自身发展有关的课程应认真听讲,汲取知识,很多老师所说的细节点,若是错过,日后有可能花大力气才发现。

Q2.如何区分一个好的程序员和不好的程序员呢?

答:个人认为程序员应具有良好的编程能力和思维,能适应各种环境,严谨思考;同时也需要正确地为人处事,明白团队配合的重要性,积极沟通;最后还需要有持续学习的好习惯,在如今时代飞速发展、技术快速更迭的背景下,持续学习才能使程序员不落后于时代。

Q3.程序员是否有必要为满足小部分人的需求去做软件,或者为软件添加某些功能?

答:个人认为应视情况而定,若这是一些较为新颖的需求,可以考虑开发,若与大部分人的需求只有一点细微的不同,显然没有必要,开发功能还应该充分分析它的可行性,性价比等等,无法一概而论。

Q4.为什么要在大学中只用一学期软件工程,时间够吗?

答:个人认为一学期学习软件工程的时间不足够,目前市面上的企业需要的是实战经验丰富的人才,只用一学期进行软件工程实战略显不足,所以还需要在课外自己另外花时间做项目,学知识,当然,知识的积累并不全来源于课程,只要肯下功夫,自主学习,大学只用一学期学软件工程并没有太大的影响。

Q5.代码量与个人的编码能力有直接关系吗?

答:这个问题因人而异,相同的代码量并不代表相同的编码能力,术业有专攻。个人认为代码量和个人编程能力有直接的关系,参与实践才能检验出真理,更何况还可以熟能生巧,不编码何谓程序员,多次反复在一些类似的技术点上花功夫,总有一天能够熟练掌握,切忌空谈理论。

4.2你未来的职业规划是什么?

未来的职业规划是从事JAVA后端开发。累积学习知识点,参与项目实战积累经验,大四实习积累经验,毕业后参加企业面试。

4.3 对于软件工程实践课程,你有什么理解和期望?

希望通过软件工程实践课程积累项目经验,熟练运用JAVA的框架和技术,例如ssm框架,springboot等。
期待助教能够分享更多有关项目实战的经验以及找工作的经验。

5.思维导图和学习路线

5.1思维导图

222100224_林然鹏_202302软件工程实践(个人)

5.2路线规划图

222100224_林然鹏_202302软件工程实践(个人)

这个作业属于哪个课程 福州大学-202302软件工程实践
这个作业要求在哪里 软件工程第二次作业–文件读取
这个作业的目标 掌握文件读取、JSON解析、单元测试
其他参考文献 单元测试、《git入门》、工程师的能力评估和发展

二、个 人 实 战

0.Gitcode项目地址

git仓库

1.PSP表格

PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 20
• Estimate • 估计这个任务需要多少时间 15 20
Development 开发 855 970
• Analysis • 需求分析 (包括学习新技术) 30 30
• Design Spec • 生成设计文档 15 25
• Design Review • 设计复审 10 15
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 20 15
• Design • 具体设计 30 20
• Coding • 具体编码 600 720
• Code Review • 代码复审 30 25
• Test • 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告 90 85
• Test Repor • 测试报告 60 50
• Size Measurement • 计算工作量 15 20
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 15 15
合计 965 1075

2.解题思路描述

2.1 问题1 JSON数据解析,查找players信息

1.在IDEA编译器里使用“Ctrl+Alt+L”命令格式化JSON文件,根据结构分析所需数据的位置。
分析出树形结构:JSON -> CountryName/Participations -> Gender/PreferredLastName/PreferredFirstName
找到输出players信息所需的Country、Gender、Full Name字段。

2.导入fastjson的jar包,用于帮助解析JSON数据,常用API有 parseObject、getJSONObject、getJSONArray等。
222100224_林然鹏_202302软件工程实践(个人)

2.2 问题2 JSON解析比赛项目,查找比赛结果

1.JSON解析方法同上,并通过event.json查找赛事项目ID,将赛事ID作为路径的一部分,打开对应的datas/result里的赛事结果json文件。

2.赛事项目文件event.json中查找项目ID:
event.json -> Sports -> DisciplineList -> Id/DisciplineName;
222100224_林然鹏_202302软件工程实践(个人)

3.比赛结果文件中查找Full Name、Rank、Score:
json -> Heats -> Results -> Rank/FullName/TotalPoints/Dives -> DivePoints.
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

3.接口设计和实现过程

3.1 接口Core的设计

设计一个Core接口,作为运算核心独立出来,内部实现三个静态方法供其他类调用。程序对指令判断正误后,根据判断结果调用Core中的计算方法,并将结果写入文件中。

1.静态方法writePlayers:根据要求筛选信息后将运动员的部分信息写入文件中。
2.静态方法writeResults:该方法首先在JSON文件中寻找项目ID,再根据项目ID寻找对应的比赛结果JSON文件,最后从该json文件解析出所需的各种信息并写入文件中。
3.静态方法readJsonFile:作用于writePlayers和writeResults方法中,用于打开JSON文件并返回JSON字符串。

3.2 代码组织及接口实现过程

代码组织:

DWASearch类作为程序主入口,进入程序后创建一个Lib类,该类作为中间类,其主要职责有以下三个(分别对应三个函数):
1.读取input.txt文件,将其所有commands存储下来。
2.判断commands的正确性。
3.调用Core运算核心,利用Core里的算法搜索需要的信息并将结果写入文件。

接口实现:

1.readJsonFile(String path)函数:用FileReader打开JSON文件并读取内容,将其保存为字符串后返回给调用函数。
2.writePlayers(String filename)函数:利用上面的readJsonFile函数,打开运动员信息对应的JSON文件,利用fastjson里的parseObject、getJSONObject、getJSONArray等常用API解析数据,将获取的数据用StringBuilder存储,再利用文件流BufferedWriter写入文件.
3.writeResults(String command, String filename)函数,与上面的writePlayers函数大同小异,不同点是多一步从项目信息event.json文件取出项目ID,根据项目ID找到对应的比赛结果JSON文件,解析JSON文件后写入output文件。

4.关键代码展示

4.1 输出players信息

	static void writePlayers(String filename) {
        try {
            //获取.json文件路径
            String path = JSON.class.getClassLoader()
                    .getResource("src/datas/athletes.json")
                    .getPath();
            //.json文件转换成json字符串
            String json = readJsonFile(path);
            // 将json文件内容转换成JSONArray对象
            JSONArray jsonArray = JSON.parseArray(json);

            //output文件输出对象创建
            BufferedWriter bw = new BufferedWriter(
                    new FileWriter(filename, true));
            //一个StringBuilder,存储json字符串,放入HashMap
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String country = jsonObject.get("CountryName").toString();
                JSONArray athletes = jsonObject.getJSONArray("Participations");
                for (int i1 = 0; i1 < athletes.size(); i1++) {
                    JSONObject athlete = athletes.getJSONObject(i1);
                    String lastName = athlete.get("PreferredLastName").toString();
                    String firstName = athlete.get("PreferredFirstName").toString();
                    String gender = athlete.get("Gender").toString();
                    if (gender.equals("0")) {
                        gender = "Male";
                    } else {
                        gender = "feMale";
                    }
                    sb.append("Full Name:" + lastName + " " + firstName + "
"
                            + "Gender:" + gender + "
" + "Country:" + country + "
"
                            + "-----" + "
");
                }
            }
            //放入map,后续直接从map取出json字符串,省略计算过程。
            Lib.map.put("players", sb.toString());
            bw.write(sb.toString());
            bw.flush();
            bw.close();
        } catch (Exception e) {
            System.out.println("输出players信息时,json文件或output文件打开错误。");
        }
    }


123456789101112131415161718192021222324252627282930313233343536373839404142434445

4.2 输出比赛结果

static void writeResults(String command, String filename) {
        String resultId = "";
        //isError判断项目名称是否无效
        boolean isError = true;
        try {
            //以下代码块根据赛事名字提取对应Id
            String path = JSON.class.getClassLoader()
                    .getResource("src/datas/event.json")
                    .getPath();
            //.json文件转换成json字符串
            String json = readJsonFile(path);
            // 将json文件内容转换成JSONObject对象
            JSONObject jsonObject = JSON.parseObject(json);
            String s = command.substring(7);
            JSONArray sports = jsonObject.getJSONArray("Sports");
            JSONObject object = sports.getJSONObject(0);
            JSONArray disciplineList = object.getJSONArray("DisciplineList");
            for (int i = 0; i < disciplineList.size(); i++) {
                JSONObject lastObject = disciplineList.getJSONObject(i);
                if (lastObject.get("DisciplineName").toString()
                        .toLowerCase()
                        .equals(s)) {
                    resultId = lastObject.get("Id").toString();
                    isError = false;
                }
            }
            if (isError) {
                try {
                    BufferedWriter bw = new BufferedWriter(
                            new FileWriter(filename, true));
                    bw.write("N/A" + "
");
                    bw.write("-----" + "
");
                    bw.flush();
                    bw.close();
                    return;
                } catch (Exception e) {
                    System.out.println("对于无效指令,写入N/A时,打开文件错误。");
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            System.out.println("根据指令查找项目ID时,打开event.json文件错误,检查路径。");
        }
        try {
            //以下代码块进入某一Id的.json文件提取需要的信息
            //获取.json文件路径
            String path2 = JSON.class.getClassLoader()
                    .getResource("src/datas/results/" + resultId + ".json")
                    .getPath();
            //.json文件转换成json字符串
            String jsonString = readJsonFile(path2);
            // 将json文件内容转换成JSONObject对象
            JSONObject jsonObject1 = JSON.parseObject(jsonString);
            JSONArray heats = jsonObject1.getJSONArray("Heats");
            JSONObject heatsObject = heats.getJSONObject(0);
            JSONArray results = heatsObject.getJSONArray("Results");
            ArrayList<String> arrayList = new ArrayList<>();
            BufferedWriter bw = new BufferedWriter(
                    new FileWriter(filename, true));
            //一个StringBuilder,存储json字符串,放入HashMap
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < results.size(); i++) {
                JSONObject resultObject = results.getJSONObject(i);
                String fullName = resultObject.get("FullName").toString();
                String rank = resultObject.get("Rank").toString();
                String totalPoints = resultObject.get("TotalPoints").toString();
                JSONArray dives = resultObject.getJSONArray("Dives");
                for (int i1 = 0; i1 < dives.size(); i1++) {
                    JSONObject diveObject = dives.getJSONObject(i1);
                    arrayList.add(diveObject.get("DivePoints").toString());
                }
                sb.append("Full Name:" + fullName + "
" + "Rank:" + rank + "
");
                sb.append("Score:");
                for (int i1 = 0; i1 < arrayList.size(); i1++) {
                    sb.append(arrayList.get(i1));
                    if (i1 != arrayList.size() - 1) {
                        sb.append(" + ");
                    } else {
                        sb.append(" = ");
                    }
                }
                sb.append(totalPoints + "
" + "-----" + "
");
                arrayList.clear();
            }
            Lib.map.put(command, sb.toString());
            bw.write(sb.toString());
            bw.flush();
            bw.close();
        } catch (Exception e) {
            System.out.println("打开比赛result的json文件时,文件打开错误,检查路径是否正确。");
        }
    }


123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293

5.性能改进

分析:每次读取一条有效指令就要进行一次运算,相同的指令重复运算,浪费时间,考虑使用HashMap存储<指令,数据>键值对,再次碰到指令时,从HashMap内取出数据,省略运算。

修改前:
222100224_林然鹏_202302软件工程实践(个人)

修改后:

222100224_林然鹏_202302软件工程实践(个人)


222100224_林然鹏_202302软件工程实践(个人) 修改后测试:
222100224_林然鹏_202302软件工程实践(个人)

前后对比:
222100224_林然鹏_202302软件工程实践(个人)

222100224_林然鹏_202302软件工程实践(个人) 结果:速度提高了85ms,性能提升约20% 。

6.单元测试

导入junit的jar包进行单元测试,新建一个test包,包内创建测试类并编写@Test修饰的函数进行测试。

6.1 DWASearch类测试

写入不同的文件名,以个数和文件名的变化为条件设计测试用例。

 @Test
    public void testDWASearch1() {
        DWASearch.main(new String[]{"input.txt"});
    }

    @Test
    public void testDWASearch1_1() {
        DWASearch.main(new String[]{"input.1"});
    }

    @Test
    public void testDWASearch1_2() {
        DWASearch.main(new String[]{"input.a"});
    }

    @Test
    public void testDWASearch2() {
        DWASearch.main(new String[]{"input.txt", "output.txt"});
    }

    @Test
    public void testDWASearch2_1() {
        DWASearch.main(new String[]{"input.txt", "output.a"});
    }

    @Test
    public void testDWASearch2_2() {
        DWASearch.main(new String[]{"input.q", "output.txt"});
    }

    @Test
    public void testDWASearch2_3() {
        DWASearch.main(new String[]{"input.a", "output.a"});
    }

    @Test
    public void testDWASearch3() {
        DWASearch.main(new String[]{"input.txt", "output.txt", "aaa"});
    }

    @Test
    public void testDWASearch3_1() {
        DWASearch.main(new String[]{"input.txt", "output.a", "input.txt"});
    }


1234567891011121314151617181920212223242526272829303132333435363738394041424344

以下是部分测试覆盖率截图:

testDWASearch1:缺少输出文件,参数不完整,故没有进入Core运算核心。
222100224_林然鹏_202302软件工程实践(个人)testDWASearch2:输入正确,覆盖率高。
222100224_林然鹏_202302软件工程实践(个人)testDWASearch3_1:输入多余参数且输出文件名错误,但不影响执行,会生成错误名称的output文件并写入。
222100224_林然鹏_202302软件工程实践(个人)

6.2 Core运算核心接口测试

根据输出文件名和指令的正误为条件,编写测试用例。

 @Test
    public void testCore1() {
        //正确的输入
        Core.writePlayers("output.txt");
        Core.writeResults("result women 1m springboard","output.txt");
    }

    @Test
    public void testCore2() {
        //文件名错误,结果会产生一个新文件并写入
        Core.writePlayers("output111.p");
        Core.writeResults("result women 1m springboard","output222");
    }

    @Test
    public void testCore3() {
        //文件名正确,指令错误
        Core.writeResults("18888","output.txt");
    }

    @Test
    public void testCore4() {
        //文件名与指令均错误
        Core.writePlayers("output333");
        Core.writeResults("result women 1m springboard","output444.p");
    }


1234567891011121314151617181920212223242526

以下是部分测试覆盖率截图:

testCore1:输入正确,Core运算核心覆盖率高。
222100224_林然鹏_202302软件工程实践(个人)testCore3:文件名正确,指令错误,运算过程少,覆盖率较低。
222100224_林然鹏_202302软件工程实践(个人)testCore4:文件名错误,指令正确,生成错误文件名文件并写入,覆盖率高。
222100224_林然鹏_202302软件工程实践(个人)
提高覆盖率:编写全面、有效的测试用例,覆盖系统的各个功能模块和边界条件,确保测试能够尽可能地覆盖系统的各个方面。

7.异常处理

主要在文件打开和写入时进行异常处理,以及少部分数组越界异常处理,处理时抛出异常并提示异常原因。
例子如下:

static void writeResults(String command, String filename) {
        String resultId = "";
        //isError判断项目名称是否无效
        boolean isError = true;
        try {
            ...
        } catch (Exception e) {
            System.out.println("根据指令查找项目ID时,打开event.json文件错误,检查路径。");
        }
        try {
           ...
        } catch (Exception e) {
            System.out.println("打开比赛result的json文件时,文件打开错误,检查路径是否正确。");
        }
    }

123456789101112131415
static String readJsonFile(String path) {
        try {
            File file = new File(path);
            FileReader fr = new FileReader(file);
            int ch;
            StringBuilder sb = new StringBuilder();
            while ((ch = fr.read()) != -1) {
                sb.append((char) ch);
            }
            fr.close();
            return sb.toString();
        } catch (Exception e) {
            System.out.println("json文件打开错误,检查路径是否正确。");
            e.printStackTrace();
            return null;
        }
    }


1234567891011121314151617

8.心得体会

JSON解析可以帮助我们将复杂的数据结构转换为易于理解和操作的格式,方便数据传输和存储。而文件读取与写入则是实现数据持久化的关键,能够让程序在不同运行环境下保持数据的一致性。本次作业令我掌握了如何使用Java中的相关库来进行JSON解析、更加熟练文件读取与写入。

三、结对作业 1–原型设计

这个作业属于哪个课程 福州大学-202302软件工程实践
这个作业要求在哪里 结对第一次作业–原型设计
结对学号 222100224、222100225
这个作业的目标 学习页面设计工具、设计页面、结对合作
其他参考文献 《构建之法》

1.网页原型

页面原型

2.需求分析(NABCD模型)

NABCD模型

N(Need,需求)

1.需要一个直观显示世界游泳锦标赛的选手信息、每日赛程等内容的平台。2.实现平台基本功能:首页、每日赛程、详细赛况等。3.直观简洁的平台,方便与用户的交互,能够快速浏览选手信息和比赛结果。4.设计美观,图文并茂,符合大众审美。

A(Approach,做法)

1.用户为中心的设计:** 专注于创建直观和用户友好的平台,设计直观、易于导航的用户界面,使用户能够轻松浏览和查找所需信息。2.将需求划分为首页,每日赛程,详细赛况,选手信息,了解更多五个模块,在导航栏展示,使用户体验良好。3.每日赛况中提供修改日期按钮,选择日期查看比赛情况,并可点击跳转至详细赛况查看比赛信息。4.选手信息页面根据不同运动赛事分类参赛者并展示参赛者的国籍、姓名、性别等信息。5.了解更多页面描述世界游泳锦标赛相关历史与内容,并提供其他相关网站链接,便于用户了解更多信息。

B(Benefit,好处)

1.用户体验良好:** 用户将可以访问一个视觉吸引人且易于导航的平台,获取所有与赛事相关的信息。2.网页在浏览器查看,不局限平台。3.网页以中文为主导语言,对中国用户更加友好。

C(Competitors,竞争)

1.提供类似平台的各大厂商竞争对手。

2.赛事官网。

3.其他结对小组,功能需求类似,竞争点在于页面设计美观以及扩展功能。

优势:
1.页面直观简介,导航栏清晰明了,便于与用户的交互,体验感好。
2.美观,符合大众审美,功能完善,符合需求。
3.包含选手信息等扩展页面。
4.成本低,浏览器直接访问,不受限于平台。

劣势:
1.数据较为固定,目前无法实时更新数据,信息较为落后。
2.宣传不足,知道网页人数较少,网页冷门。
3.开发起步晚,市场份额小,团队简单,无强大竞争力。
4.缺乏相关平台开发经验。

D(Delivery,推广)

1.介绍给同学,互相宣传浏览。2.以视频,博文等形式通过微信公众号,抖音等社交平台宣传网页。

3.模型设计

3.1原型开发工具

学习使用墨刀网页版进行页面原型的开发设计,自主寻找资源学习墨刀工具。利用墨刀的实时在线共同设计模式,与伙伴共同设计页面。

3.2页面原型设计过程

需求分析,划分模块
首页:展示世界游泳锦标赛的标题与年份,设计背景图与整体大致框架,添加导航栏并逐步实现功能与页面跳转。每日赛程:设计日期选择的按钮,根据日期的切换展示不同的赛事,赛事可点击进入详细赛况页面,查看赛事目前的详细结果。详细赛事:赛事页面根据不同赛事种类进行划分,罗列出各项赛事,供用户选择查看哪一个赛事的详细情况,根据用户的选择跳转页面显示比赛结果。选手信息:选手信息页面根据不同赛事进行分类,展示出各个赛事的参赛选手的国籍、姓名、性别等信息,供用户查看。了解更多:展示了世界游泳锦标赛的历史与发展等信息,内置页面跳转,链接其他信息页面,供用户跳转页面了解更多有关信息。
任务分配,共同设计
任务分配,约定好每个人设计的模块,利用墨刀的共享设计,共同设计页面,实时交流;最后将设计完的页面生成链接以供分享、查看。

4.结果汇报

4.1页面设计成果

首页:包含导航栏、最新消息、视频、图片等模块。

222100224_林然鹏_202302软件工程实践(个人) 222100224_林然鹏_202302软件工程实践(个人)


每日赛程 :可根据星期选择查看每日赛事,附带查看详情按钮,点击可查看比赛详情。
222100224_林然鹏_202302软件工程实践(个人)


详细赛况:根据比赛进行分类,用户可根据喜好选择自己想查看的赛事情况。
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

了解更多:展示了世界游泳锦标赛的历史与发展等信息。

222100224_林然鹏_202302软件工程实践(个人)

4.2遇到的困难及解决方法

遇到的困难:

P1.第一次使用墨刀,需要自主查找资源学习使用。P2.页面样式的设计,需要符合大众审美,简洁明了,方便且易于交互。P3.各种组件按钮的事件处理,事件较多且易混。P4.页面动态效果的设计,合适的组件选择,导航菜单中如何添加表格等复杂的内容。P5.与队友第一次配合,设计、时间安排、各种问题解决方法等方面需要沟通。


解决方法

S1. 学习墨刀的使用:

自主学习,寻找资源:

官方文档和教程: 墨刀的官方网站提供了详细的文档和教程,可以让我们快速入门。视频教程: 在bilibili或其他在线教育平台上搜索墨刀教程,观看视频,更直观地理解墨刀的使用方式。社区和论坛: 参与墨刀的社区和论坛,与其他用户交流经验和学习心得。
结果:该问题良好解决,学会了使用大部分墨刀基础功能。

S2. 页面样式的设计:

符合大众审美: 简洁明了的设计是关键,避免过多的装饰和复杂的布局。易于交互: 使用明确的导航栏和按钮,保持页面结构清晰,确保用户可以轻松找到需要的信息;使用大众熟悉的布局和颜色搭配,确保用户能够快速理解和使用页面。
结果:在交互设计上,考虑用户的操作习惯和便利性,保证页面易于操作和流畅性。
设计达成共识,问题良好解决,页面清晰简洁。

S3. 各种组件按钮的事件处理:

事件处理的清晰性: 给每个组件按钮分配清晰的功能,避免功能重叠或混淆,使用墨刀的事件处理功能,可以将事件和按钮进行关联,实现交互效果,方便管理和调试。使用命名规范: 为每个事件命名,采用易于理解的命名,让团队成员能够快速理解各个事件的功能。
结果:根据自己的设计模块进行事件处理并积极沟通配合,问题良好解决。

S4. 页面动态效果的设计与组件选择:

合适的组件选择: 根据页面需求和用户体验,选择合适的动态效果组件,增强页面的交互性和吸引力。动画效果设计: 选择一些常用的动画效果组件,如滑动、淡入淡出等,保持页面简洁和流畅;在设计动态效果时,要注意与页面内容的结合,突出重点信息,提升用户体验和页面吸引力。
结果:设计结果良好,包含一些动态效果,富有新意。在组件选择方面,导航菜单这一组件的内容如何添加表格以展示数据是一个困难,寻找了解决方法但结果不尽人意,故采用页面跳转的方式实现赛事详细情况的展示。

S5. 与队友沟通协作:

沟通交流: 舍友之间在设计过程中随时交流,及时探讨问题,给出解决方案,讨论页面设计的方向和细节,确保设计目标达成共识。时间安排: 制定清晰的时间安排和任务分配,确保两人都知道自己的任务模块。
结果:结对队友为相处三年的舍友,配合沟通积极且方便,尽管有困难,但及时沟通,总能解决。

4.3收获心得

学习能力的提升: 通过自主学习墨刀等工具的使用,学习能力得到了提升。掌握新工具和技能,不断进步。

设计理念的建立: 在页面样式设计中,学会开始建立起自己的设计审美,明白简洁明了、大众审美的重要性,以及用户体验的核心地位。

与他人协作能力的加强: 与队友的合作让我们学会了有效沟通和协作。团队的成功不仅仅取决于个人能力,更在于团队成员之间的配合和信任。

问题解决能力的提升: 面对各种技术和设计问题,寻找解决方案也是一种锻炼。解决问题的过程不仅仅是解决具体的技术难题,更是锻炼了逻辑思维和创造力。

5.效能分析和PSP表格

5.1任务分配与合作过程

​ 在拿到作业需求的第一时间,我们就展开了任务需求的理解,并在理解任务要求后分配好了各自的任务。

界面设计:在两人一起讨论完大概方向后,由林璞先给出初版原型,包括页面大致布局及页面数量,在这之后利用墨刀的在线协同设计,一起完善这个初版原型,最后由林璞进行整合。

博客方面,则是由林然鹏给出大致模板,各自选择编写部分,最后由林然鹏进行整合,至此为本次作业的大概分工。

在实际完成作业中,难免各自部分都会有相应的问题,由于我们两个为舍友,故这部分问题由两个人线下讨论解决。

原型设计过程照片:

222100224_林然鹏_202302软件工程实践(个人)

设计、博客整合

222100224_林然鹏_202302软件工程实践(个人)

5.2队友评价

林璞 -> 林然鹏:

原型设计:积极参与原型设计过程,能主动提出许多合理的,对设计有帮助的意见,提高了原型设计的完成度。博客编写:给出了详细的博客大纲,最后的整合也完成的很好。

林然鹏 -> 林璞:

原型设计:在结对讨论后给出了一个比较明确的初版设计,让我可以直观的明白每一个网页的作用,为后续设计提供了便利。

博客编写:能根据博客大纲合理的编写博客,内容也都是实际体验下来之后的感受。

5.3PSP表格

PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Analysis 需求理解 40 35
Learning 学习原型设计工具 70 60
Discussion 结对讨论 40 40
Design 原型设计 40 40
Create 原型实现 150 120
Test & Improve 测试加改进 30 30
Postmortem 事后总结 30 30
合计 400 355

5.4效能分析

本次作业实际难度不大,主要难点在于原型设计工具的使用,由于以前没有接触过这类工具的使用,所以主要耗时都在这一部分。因此在使用之前我们对墨刀先进行了一个简单学习,了解了其大概使用方式后再着手设计,但毕竟是第一次使用,故还是碰到了许多问题,比如没法很好的用墨刀展现出自己的想法,这部分问题通过上网查阅资料得以解决。

除此之外,在设计过程中,两个人的想法难免会有分歧,如何较好地整合二者的想法也是一个难点。好在我们两人是同一个宿舍的,讨论问题时候比较方便,这部分的问题一般在讨论之后都能得到解决。

四、结对作业 2–编程实现

这个作业属于哪个课程 福州大学-202302软件工程实践
这个作业要求在哪里 结对第二次作业–编程实现
结对学号 222100224、222100225
这个作业的目标 学习前端页面的编写、JSON数据处理、页面渲染、前后端信息传输
其他参考文献 《构建之法》

一、 Gitcode地址

222100224-Gitcode
助教-Gitcode

二、PSP表格

PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Analysis 需求分析 60 40
Learning Vue/Springboot框架学习 200 200
Discussion 结对讨论/接口设计 60 40
Design Vue前端页面设计 400 400
Create Springboot后端实现 300 300
Test & Improve 前后端联调 120 120
Server deployment 服务器部署 180 200
Postmortem 总结 20 20
Blog writing 博客编写 50 50
合计 1390 1370

三、项目访问链接

222100224_222100225-结对项目链接

四、项目展示

具体效果请点击跳转网页查看

4.1 首页

首页在页头的基础上添加一个轮播图,展示了有关比赛的一些图片。
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

4.2 每日赛事

根据导航栏所选择的比赛类型,展示比赛时间,支持点击跳转查看详细赛事。
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

4.3 详细赛况

根据导航栏选择的比赛类型,展示该分类下包含的赛事。进一步点击赛事折叠面板查看详细比赛结果。比赛结果分为半决赛,总决赛等多种,支持赛程切换。
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

4.4 选手信息

根据比赛类型展示本类型的参赛选手信息,包括国籍、性别、出生日期。支持切换比赛类型查看不同比赛的选手信息。
222100224_林然鹏_202302软件工程实践(个人)

4.5 奖牌榜

根据最终排名顺序展示国家获奖情况;展示各个国家的总奖牌数、金牌、银牌、铜牌的数量。
222100224_林然鹏_202302软件工程实践(个人)

4.6 了解更多

展示有关本次赛事的信息;末尾提供本次赛事官网跳转链接以便用户查看更多信息。
222100224_林然鹏_202302软件工程实践(个人)

五、结对讨论描述

(1)首先确定使用的框架以及技术,springboot框架先前已有所涉猎;故本次作业主要问题在于前端的技术实现,最终在宿舍里商讨决定使用Vue框架配合Element组件进行前端页面的制作,再通过后端Springboot传递数据给前端页面进行渲染。

(2)第二步是需求分析与设计接口,先初步分析需要的数据,根据作业要求设计接口文档,在编写过程中进一步优化与联调。

交流截图(Gitcode分支;初步接口文档;联调URL):222100224_林然鹏_202302软件工程实践(个人)

(3)接口文档最终在Swagger上呈现,前后端联调只需要连接同一个局域网,并访问对方的IP地址及端口即可连接到队友,通过这种方式进行联调并查看Swagger上的接口文档。

Swagger上的接口文档截图:
222100224_林然鹏_202302软件工程实践(个人)

(4)各自编写代码,在同一局域网下只需开启服务器就可以相互进行联调测试,逐步修改bug完善项目。

(5)服务器部署,参照网上服务器部署教学资源学习服务器部署,选用阿里云的服务器,最终成功部署前后端。

前后端分别部署在两台服务器上,互相给出服务器IP地址,连接对方服务器。
222100224_林然鹏_202302软件工程实践(个人)

(6)结对讨论:实际上为舍友,直接了当的语言沟通。

222100224_林然鹏_202302软件工程实践(个人)

六、实现过程&功能结构图

开发具体步骤如下:

前端开发(使用Vue和Element组件):

创建首页及通用页头ElementView。创建选手信息页面:展示所有选手的信息,使用Element的el-table组件实现表格展示。创建每日赛况页面:创建一个比赛列表,展示比赛的名称及日期,对于决赛加粗并红色突出显示,el-table的点击事件完成赛事的点击查看详细情况。创建详细赛况页面:展示比赛的名称在折叠面板el-collapse上,点击可下拉展示详细内容;点击下拉后可以通过el-tags组件选择赛程并通过el-table组件展示其比赛结果,包括选手排名,比赛积分,落后积分等信息。创建奖牌榜页面:展示各个参赛国的获奖情况,直观展示国家排名与各个国家奖牌的获取情况。创建了解更多页面:展示一些有关本次比赛的信息,并在最后提供官网链接以便查看更多相关信息。

后端开发(Springboot框架)

创建选手信息API:实现获取所有选手信息的接口,包括Country,Athlete,Gender,DOB等信息,封装后返回给前端。创建每日赛况API:实现获取每日赛事的接口,根据比赛类型分类,封装比赛时间、比赛进程等信息给前端渲染。创建详细赛况API:第一个接口根据用户选择的赛事类别,封装本赛事类别下的所有赛事给前端渲染,供用户选择查看哪一个具体赛事;第二个接口根据用户点击的具体赛事的折叠面板,前端传来具体赛事名称,根据赛事名称提取该赛事的选手排名、比赛积分、落后积分等比赛信息,封装完成后返回前端。创建奖牌榜API:提取各个参赛国获得的奖牌情况与奖牌数量排名进行封装后返回前端渲染。

开发完成后进行前后端联调与服务器部署:

前后端联调在同一局域网下互相访问即可联调。服务器部署在初始化服务器后将后端jar包与前端打包完的dist包上传到服务器,修改配置后启动nginx渲染前端,启动jar包运行后端即可正常访问页面。

功能结构图:
222100224_林然鹏_202302软件工程实践(个人)

七、关键代码展示

7.1前端关键代码

1.请求事件

使用element组件需要编写一些点击事件从后端接受数据并渲染。



export default {
 
  methods: {
    handleSelect(key, keyPath) {
      console.log(key, keyPath);
    },
    //Tabs点击事件
    tabHandleClick() {
      axios
        .get(MyUrl.urlDetail, {
          params: { event: this.activeName },
        })
        .then((result) => {
          this.items = result.data.data;
        });
    },
    //折叠面板点击事件
    handleChange() {
      this.tabActiveName2 = "Finals";
      if (this.collapseActiveName != null && this.collapseActiveName != "") {
        axios
          .get(MyUrl.urlDetails, {
            params: {
              event: this.activeName,
              sportsName: this.collapseActiveName,
            },
          })
          .then((result) => {
            this.tabItems = result.data.data.map((item) => item.phaseName);
            this.result2 = result.data.data;
            this.tableData2 = this.result2.filter(
              (item) => item.phaseName === this.tabActiveName2
            )[0].players;
            console.log(
              this.tabActiveName2 + "tableData21->" + this.tableData2
            );
          });
      }
    },
    //tab2点击事件
    tabHandleClick2() {
      this.tableData2 = this.result2.filter(
        (item) => item.phaseName === this.tabActiveName2
      )[0].players;

      console.log(this.tabActiveName2 + "tableData22->" + this.tableData2);
    },
  },
  //钩子方法
  mounted() {
    axios
      .get(MyUrl.urlDetail, {
        params: { event: this.activeName },
      })
      .then((result) => {
        this.items = result.data.data;
      });
  },
};
</script>


1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
2.页面代码

页面的制作需要注意其格式排版,不断微调以达到最好的展示。以下是一小部分页面绘制的代码:

<!-- 折叠面板 -->
    <el-collapse
      v-model="collapseActiveName"
      accordion="true"
      @change="handleChange"
      style="margin-left: 200px; margin-right: 200px"
    >
      <el-collapse-item
        v-for="(item, index) in items"
        :key="index"
        :title="item"
        :name="item"
        style="margin-left: 20px; margin-top: 15px"
        ><template #title>
          <span
            style="
              color: #3299cc;
              font-weight: bold;
              font-size: 1.5em;
              padding-left: 110px;
            "
            >{{ item }}</span
          >
        </template>
        <!-- 折叠面板内部标签 -->

        <el-tabs
          v-model="tabActiveName2"
          @tab-click="tabHandleClick2"
          style="position: relative; width: 380px; left: 30%; top: -10px"
        >
          <el-tab-pane
            v-for="(item, index) in tabItems"
            :key="index"
            :label="item"
            :name="item"
          ></el-tab-pane>
        </el-tabs>

        <!-- 内层表格渲染 -->
        <el-table
          :data="tableData2"
          border
          style="
            margin-left: 110px;

            margin-top: 10px;
            padding-top: 10px;
          "
        >
          <el-table-column prop="overallRank" label="选手排名" width="200px">
          </el-table-column>
          <el-table-column prop="fullName" label="参赛选手" width="200px">
          </el-table-column>
          <el-table-column
            prop="points"
            label="比赛积分/比赛用时"
            width="200px"
          >
          </el-table-column>
          <el-table-column prop="pointsBehind" label="落后积分" width="200px">
          </el-table-column>
        </el-table>
      </el-collapse-item>
    </el-collapse>


1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465

7.2后端关键代码

1封装文件操作代码
1.1文件提取:

以下两个为示例:主要就是根据传递进来的参数值去访问不同的文件,然后返回一个流,便于后续将json文件转为字符串

@ApiOperation("传入赛事类别,获取选手信息文件")
public static InputStream getAthletesFile(String event){
    String fileName = event + "/athletes.json";
    InputStream input = ClassUtils
            .getDefaultClassLoader()
            .getResourceAsStream("results/athletes/" + fileName);
    return input;
}
    @ApiOperation("获取项目文件")
    public static InputStream getSportsDetailsFile(String event,String sportsName){
        String fileName = event + "/" + sportsName + ".json";
        InputStream input = ClassUtils
                .getDefaultClassLoader()
                .getResourceAsStream("results/events/" + fileName);
        return input;
    }


12345678910111213141516
1.2将json文件转为字符串:

利用流读取文件,将其拼接在StringBuffer对象后面,再利用toString方法进行转换

@ApiOperation("将json文件转为字符串")
public static String JSONToString(InputStream inputStream)  {
    String finalStr = "";
    StringBuffer jsonStrBuffer = new StringBuffer();
    try{
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

        int data;
        if(bufferedInputStream != null){
            while((data = bufferedInputStream.read()) != -1){
                jsonStrBuffer.append((char) data);
            }
            finalStr = jsonStrBuffer.toString();
        }
        bufferedInputStream.close();
    }
    catch (FileNotFoundException e){
        e.printStackTrace();
    }
    catch(IOException e){
        e.printStackTrace();
    }
    return finalStr;
}


123456789101112131415161718192021222324
2.网页接口编写

这里根据前端页面主要分为4个部分,选手信息、国家奖牌、赛事详情、赛事时间表这几个功能,每个功能编写一个接口,这里就展示一个接口。

public List<MatchSchedule> showEventSchedule(String event) {
    String jsonStr = fileUtils.JSONToString(fileUtils.getTimeScheduleFile(event));
    JSONArray jsonArray = JSON.parseArray(jsonStr);
    List<MatchSchedule> matchSchedules = new ArrayList<>();
    for(int i = 0; i < jsonArray.size(); i++){
        JSONObject object = jsonArray.getJSONObject(i);
        MatchSchedule matchSchedule = new MatchSchedule();
        matchSchedule.setDisciplineName(object.get("DisciplineName").toString());
        matchSchedule.setDate(object.get("Date").toString());
        matchSchedule.setPhaseName(object.get("PhaseName").toString());
        matchSchedules.add(matchSchedule);
    }
    return matchSchedules;
}

1234567891011121314

这是时间表接口里面的展示时间表接口,主要就是根据传过来的json字符串,根据格式,提取所需要的信息,将其封装,返回给前端。

3.返回数据格式封装

由于返回给前端的数据格式比较统一,故封装成一个类,之后调用类里面的静态方法传递数据。

这里偷了个懒,只处理了成功数据的返回

public class R<T> {
    private String msg;
    private Integer code;
    private T data;


    public static <T> R success(T data){
        return new R("操作成功",200,data);
    }
}

12345678910

八、总结收获&评价队友

后端开发(222100225_林璞):

8.1.1后端开发总结收获

本次结对编程,我们组是采用前后端分离的模式,后端数据存储是直接爬取json数据,保存在本地,没有封装到数据库里,有点像第二次作业的文件读取 ,后端提取完数据后,再根据前端所需,传递相应封装好的数据。

其实后端接口基本编写是很快的,因为不需要去跟数据库做交互,实际上没有什么接口。很大一部分时间都是花在调试上面。将java项目导成jar包后,部署到了服务器后,很多文件读写的操作都会报错。

222100224_林然鹏_202302软件工程实践(个人)

就比如上面这个函数,是去文件中读取对应的赛事详情,一开始是采用File类,根据路径得到相应文件,再将文件内容传给处理接口去处理,在本地调试很顺利,一遍就成功了,在部署到服务器上后,疯狂报错… 之后便是“面向CSDN编程‘,上网查询资料,整合大家踩过的坑,最后决定采用读取文件流的形式,这样子就不需要得到文件的具体路径了,直接通过流传递文件,排除了bug。

此外,在这次开发过程中,我们小组使用了swagger + knife4j去生成接口文档,该文档集成了接口参数说明,接口请求路径说明等等,此外还有在线api调试功能,给我们前后端联调提供了很多帮助(虽然一开始老是找不到自己的ip地址罢了)。

222100224_林然鹏_202302软件工程实践(个人)

在部署服务器方面,感谢阿里云的学生免费服务器以及其配套的配置教程,部署起来非常快,也很方便。由于这个项目比较基础,所以跟着网上教程配置,很快就完成了项目的部署。

总而言之,本次结对编程还是学到了很多东西,尤其是linux的指令操作,掌握一些常用的指令,然后对java文件流操作也更加熟练了,除此之外,自己整合资料的能力也得到了锻炼(网上七七八八的解决方案看的头疼…)。总的来说,还算是一次收获颇多的编程作业。

8.1.2对队友的评价

在前后端联调的过程中,我这边接口有的地方没写清楚,及时与我沟通,解决问题,积极参与讨论功能点的设计和实现,代码编写遵守代码规范,代码提交遵守规定时间,不拖拉。

前端开发(222200224_林然鹏):

8.2.1前端开发总结收获

这次开发,同样是有不少的困难。首先难点在于前端知识的匮乏,先前学过的SpringBoot框架主要是基于后端的开发,对前端的认知还停留于传统三大件,为了完成本次作业,临时加班学习了Vue框架的使用以及部署,在NPM安装Vue的过程中也有一些小问题需要解决,学习完了Vue后再尝试使用Element组件,利用组件绘制页面并不断调整页面样式,较花时间。

页面绘制完成后,还需要根据接口文档编写一系列的事件响应接收参数,对于后端传来的数据,如果结构稍微复杂一点,就需要在方法中进行分析拆解,容易混淆。

最后是服务器的部署,因为是第一次部署服务器,需要一点一点看文档学习部署,并且使用的是一点页不熟悉的Linux指令,操作起来有些许困难,进度缓慢。
本次作业收获不少,尝试了Vue绘制前端页面,尽管很花时间进行美观调整;也学习了有关服务器部署的步骤,是一次很宝贵的开发经验。

8.2.2对队友的评价

编写了较完善的接口文档,方便前端阅读,积极参与讨论功能点的设计和实现,代码编写遵守代码规范,代码提交遵守规定时间。

五、软件评测

这个作业属于哪个课程 福州大学-202302软件工程实践
这个作业要求在哪里 软件工程实践——软件评测作业
这个作业的目标 测评软件,从间接经验中学习,分析
其他参考文献 《构建之法》、软件分析和用户需求调查

1. 调研与测评

1.1 文心一言

1.1.1 产品使用体验
1.1.1.1 基本功能介绍和使用

1.问答功能,包含上传图片和文件进行对话:

大语言模型最基本的问答功能,向模型提问,获得可参考的回答。
222100224_林然鹏_202302软件工程实践(个人)AI画图功能
222100224_林然鹏_202302软件工程实践(个人)

2.选择插件

插件的选择,选择合适的插件,便于生成更好的回答。
222100224_林然鹏_202302软件工程实践(个人)使用商业信息查询:222100224_林然鹏_202302软件工程实践(个人)

3.左侧工作台,包括查询历史记录,新建对话。

历史记录展示与搜索:
222100224_林然鹏_202302软件工程实践(个人)

4.百宝箱功能

提供一系列热门的问答样例供参考,并进行了分类。
222100224_林然鹏_202302软件工程实践(个人)

1.1.1.2 优缺点分析

优点:
1.画图与问答功能响应迅速,对于问题有一定的分析能力,AI画图质量良好。2.能根据用户提出的问题进行进一步问题的联想,方便用户使用。3.百宝箱内容丰富,包含众多场景,还有众多实用功能,如代码纠错、代码转化与生成等。4.适用范围广,除了IT行业人员,一些其他群众如金融、医疗、小说创作等领域也能与文心一言沟通,获取参考。5.功能完善,界面设计良好,便于交互。 缺点:
1.对人类语言的理解能力有待加强。2.百宝箱导航栏切换时加载缓慢,容易卡顿。3.问答的准确度并不高,用户体验一般。

1.1.1.3 改进意见

1.增强对AI绘图及问答的训练,提高回答准确度。2.优化百宝箱功能的加载速度,减少卡顿情况。3.提高知识点丰富度与逻辑推理能力。

1.1.2 Bug描述

Bug严重性表格:

星级 说明
🌟🌟🌟🌟🌟 严重的安全问题:可能导致用户关键信息泄漏;严重的功能问题:某功能完全不能使用。
🌟🌟🌟🌟 潜在的安全问题:可能使用户的信息泄漏;一般的功能问题:某模块或功能在某些场合不能使用;交互问题:不符合逻辑的交互等。
🌟🌟🌟 潜在的功能问题:在某些情况下会出现的功能问题,以及一些排版、显示的错误。
🌟🌟 某些情况下反馈不符合逻辑,排版、显示错误,信息调用报错,以及在某些极端情况下功能不能使用。
🌟 细小的排版、显示错误,不影响实际信息以及主要功能。
1.1.2.1 Bug发生时的测试环境

计算机版本:Windows 11 家庭中文版浏览器:Microsoft Edge 123.0.2420.65 (正式版本) (64 位)

1.1.2.2 Bug的可复现性及具体复现步骤

可复现当让文心一言给出一些描述的相关绘图时,可能发生错误,绘制的图片与期望结果不符,对描述的提取只提取定语部分却忽略实际主语部分。复现步骤:向其提问一些定语与主语有一定区别的问题如:画一张《喜羊羊与灰太狼》中的青青草原图片。结果如下:
222100224_林然鹏_202302软件工程实践(个人)

1.1.2.3 Bug具体情况描述

向其提问是否知晓《喜羊羊与灰太狼》时,结果如下:
222100224_林然鹏_202302软件工程实践(个人)进一步提问“画一张《喜羊羊与灰太狼》中的羊村大门”时,结果不尽人意,对人类语言的理解浅显,实际上这个问题并不难理解,表述也属于清晰的范畴;包括上文的“画一张《喜羊羊与灰太狼》中的青青草原图片”提问,同样没有正确提取需求,只突出定语部分却极其简化主语部分。
222100224_林然鹏_202302软件工程实践(个人)去掉定语再次提问,结果较为准确,则说明该语言模型对定语与主语的理解存在一定的bug,在一些情况下无法提取用户实际需要的内容。
222100224_林然鹏_202302软件工程实践(个人)

1.1.2.4 Bug分析

大预言模型在理解请求时出现了误解或混淆,对一些基本的定语主语的提取出错,说明该模型缺乏训练,正确性并不高。Bug严重性:
安全性良好,系统问答功能略显低质量,用户体验感差。🌟🌟(两颗星)该Bug只是对问题的主语定语理解有问题,换一些描述就可能得到解决,但属于逻辑问题,影响用户体验度,且发生条件并不苛刻。 对于Bug的预期及改进建议
对于一些问题,应该准确把我用户的真实需求,对于简单的定语主语不应该混淆。增强AI绘画模型的训练,提取正确的需求主语。

1.1.3 用户采访

采访背景:211本科大三学生,软件工程专业。采访原因:该同学日常生活中在题目解答、代码参考、论文撰写等方面可能使用到该产品。使用功能:题目解答、代码翻译和论文撰写。使用过程中未遇到什么上手困难,使用简便。使用过程中遇到的问题:
有些问题解答不清晰,文心一言很笨,有时候不能理解我的问题和我想要的回答。信息获取问题,它获取的信息很老旧,跟不上时代,最新的问题无法解答。 改进建议:加强学习,更新它的语言模型。
222100224_林然鹏_202302软件工程实践(个人)

1.1.4 结论
1.1.4.1 定性结论

该软件实用程度良好,还不错。部分情况下用户体验较为一般。

1.1.4.2 定量结论
类别 描述 评分(每项20分)
核心功能 功能设计和质量。 17
用户细节考虑 有哪些为用户考虑的细节? 15
用户体验 用户完成功能时,不受干扰 (例如: 不断弹出不相关广告)。 14
差异化功能 软件独特功能及对用户的吸引力 16
软件效能 内存占用、启动速度、内存泄漏情况 16
总分(满分100) 78

1.2 通义听悟(与“通义千问”同源)

1.2.1 产品使用体验
1.2.1.1 基本功能介绍和使用

0.在同源的通义千问中有问答功能:
222100224_林然鹏_202302软件工程实践(个人)

1.录音并转文字,包含翻译功能。
222100224_林然鹏_202302软件工程实践(个人)

2.音视频、播客转文字,可区分发言人。
222100224_林然鹏_202302软件工程实践(个人)

3.历史记录查看
222100224_林然鹏_202302软件工程实践(个人)

4.收藏与分享功能
222100224_林然鹏_202302软件工程实践(个人)

5.支持多端登录,同时使用
222100224_林然鹏_202302软件工程实践(个人)

6.不同领域音频订阅
能够订阅不同频道,收听音频,还能根据音频转换成文本进行查看 。
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)

1.2.1.2 优缺点分析

优点
1.这个软件对于上课、开会有不小的帮助,能够录音,转文字,还能进行总结;也能对视频、播客等进行转文字、翻译、总结等,对办公室职员、大学生等众多用户提供了帮助。2.能够将对话录音保存,转文字,对采访等场景也有一定帮助。3.界面制作精美,功能划分清晰且完善,对大学生等群体来说,体验感良好,操作便捷。 缺点:
1.当录音声音较小时,文字转换的清晰度不高,可能出现文字转换错误或者遗漏文字等情况。

1.2.1.3 改进意见

1.提供更多语言选择:增加更多语言的选择,使更多用户能够使用通义听悟。2.改进用户界面:优化界面设计,使用户操作更加简单和直观。3.增加个性化设置:提供个性化设置选项,根据用户的偏好调整功能和显示。

1.2.2 Bug1描述
1.2.2.1 Bug1发生时的测试环境

计算机版本:Windows 11 家庭中文版浏览器:Microsoft Edge 123.0.2420.65 (正式版本) (64 位)

1.2.2.2 Bug1的可复现性及具体复现步骤

可复现在输入错误密码的情况下,点击登录,显示密码的小眼睛消失,无法再查看密码。只有在将密码全部删除后,重新输入密码,显示密码的小眼睛才会出现。

1.2.2.3 Bug1具体情况描述

情况1:在输入错误密码的情况下,点击登录,显示密码的小眼睛消失,无法再查看密码。情况2:在输入密码后,使用电脑截图工具,小眼睛也会消失。只有在将密码全部删除后,重新输入密码,显示密码的小眼睛才会出现;并且如果在全选密码后,不点击Backspace键,直接输入新密码,小眼睛依旧不会出现。

(1)正常情况:
222100224_林然鹏_202302软件工程实践(个人)
(2)点击登录后:
222100224_林然鹏_202302软件工程实践(个人)

1.2.2.4 Bug1分析

这个Bug可能的原因是与密码显示的逻辑有关。当密码输入框失去焦点时,密码显示的小眼睛消失,这可能是由于焦点变化触发了密码显示状态的改变。截图工具可能会触发类似的焦点变化,导致密码显示状态的改变。Bug严重性:
安全性良好,稍微影响用户体验。🌟🌟(两颗星)该Bug只是对密码的查看有少许影响,对整体功能和正常登录并没有太大影响。 对于Bug的预期及改进建议
密码显示状态管理:检查密码显示状态的管理逻辑,确保只有在用户交互下才会改变密码显示状态,而不是被外部因素(如焦点变化)所影响。事件监听:对密码输入框的事件进行监听,及时捕获用户的操作并做出相应的处理,避免不必要的UI显示改变。

1.2.3 Bug2描述
1.2.3.1 Bug2发生时的测试环境

计算机版本:Windows 11 家庭中文版浏览器:Microsoft Edge 123.0.2420.65 (正式版本) (64 位)

1.2.3.2 Bug2的可复现性及具体复现步骤

可复现修改密码后却不需要重新登录,可以继续使用;可能有安全隐患。

1.2.3.3 Bug2具体情况描述

修改密码时跳转到账号中心:
222100224_林然鹏_202302软件工程实践(个人)
222100224_林然鹏_202302软件工程实践(个人)修改密码后,不需要重新登录却可以继续使用。
222100224_林然鹏_202302软件工程实践(个人)

1.2.3.4 Bug2分析

由于账户中心是跳转到阿里云用户中心进行账户密码的修改,可能没有及时同步更新用户的登录状态,导致用户在修改密码后仍然可以保持登录状态。这可能是由于缓存或会话管理方面的问题导致的。Bug严重性:
安全性不足,较为严重,对用户体验无影响。🌟🌟🌟🌟(四颗星)该Bug可能对用户财产造成影响,比如在他人电脑上登录使用该软件,未退出登录,后续尽管修改密码,他人却能使用已登录的账户,可能对账户主人造成损失。 对于Bug的预期及改进建议
强制用户在修改密码后重新登录:建议在用户修改密码后强制要求用户重新登录,以确保用户的登录状态和密码信息是最新的。及时更新会话信息:确保在用户修改密码后,会话信息能够及时更新,使得用户的登录状态能够同步更新。检查缓存机制:检查通义听悟中的缓存机制是否会导致用户登录状态没有及时更新,确保缓存中的用户信息是最新的。

1.2.4 结论
1.2.4.1 定性结论

该软件实用程度良好,适用于上课、开会、采访、翻译等场景,需求广泛,解决的问题也很实际,能够对用户提供不小的帮助。虽然可能有一点Bug,还可能造成在其他地方登录未退出的情况,但都是一些概率极小的事件,造成的损失一般也不涉及财产损失,还是建议修改Bug,但也推荐使用。

1.2.4.2 定量结论
类别 描述 评分(每项20分)
核心功能 功能设计和质量。 18
用户细节考虑 有哪些为用户考虑的细节? 16
用户体验 用户完成功能时,不受干扰 (例如: 不断弹出不相关广告)。 16
差异化功能 软件独特功能及对用户的吸引力 18
软件效能 内存占用、启动速度、内存泄漏情况 15
总分(满分100) 83

2. 分析

2.1文心一言分析

2.1.1 同类产品对比与排名

在同类产品中,对比ChatGPT,回答的准确度还有些许不如ChatGPT,但也有自己的其他功能,比如AI绘画等,有优势也有劣势。排名个人评价:同类产品中名列约第5。(对比ChatGPT、Google Bard、Claude、通义千问、讯飞星火、kimi)

2.1.2 软件工程方面的建议

性能优化与监控:
对文心一言的性能进行持续训练优化,提高响应速度、吞吐量以及响应的准确率。
建立性能监控体系,实时监控系统的运行状态,及时发现并解决性能瓶颈。用户反馈与迭代:
建立用户反馈机制,及时收集和处理用户反馈,不断优化用户体验。
根据市场需求和技术发展,定期更新和迭代文心一言的功能和性能。

2.1.3 Bug存在的原因分析

Bug的可能原因是具体的设计质量不高;该Bug并非逻辑上的错误或程序员的不细心,可能是训练不足的问题,对语言的理解程度不足;这个Bug也是极其难以发现和修复的Bug。解决该Bug需要的是及时纠正模型的错误,将模型往正确方向引导并加以训练学习。

2.1.4 产品开发时间预估

对于一个6人的团队,个人认为开发时长大概如下表:

任务描述 花费时间/天
需求分析与文档编写 15
数据收集与处理 20
接口开发与测试 40
前端界面设计与开发 25
模型训练与优化 40
系统集成与联调 15
总计 155

产品的接口开发与测试需要花费的时间较多,且大语言模型难度本身较高,在参考其他同类型产品的情况下,依旧需要不少开发时间,根据上面表格大概可以累计出总时长。

2.2 通义听悟分析

2.2.1 同类产品对比与排名

优势:
功能全面:通义听悟集成了多种功能,如转写、检索、摘要和整理等,为用户提供全面的音视频处理体验。实时性强:对于实时的音频流或视频流,通义听悟能够快速地处理和反馈结果,满足实时应用场景的需求。准确性高:对于大部分的音频和视频内容,通义听悟能够提供准确的转写、检索、摘要和整理结果。 劣势:
处理速度:对于较长的音视频内容或方言口音较重的语音,通义听悟的处理速度可能会较慢。费用问题:作为一款云服务产品,通义听悟的使用需要支付一定的费用,可能不适合所有用户群体。 同类产品较少,相对于“讯飞听见”、“百度语音识别”等软件,功能大差不差,具体分水岭在于处理速度与处理准确度。排名个人评价:同类产品中排名约在第4名。(对比讯飞听见、百度语音识别、腾讯云智聆口语、iFlytek语音云、网易云见外工作台)

2.2.2 软件工程方面的建议

安全与隐私保护:
加强数据安全和隐私保护措施,确保用户数据的安全性和隐私性。
对外部输入进行严格的验证和过滤,防止潜在的安全漏洞和攻击。界面UI显示优化
登录界面UI有一些小Bug建议优化;另外也可以尝试对首页进行优化,使布局更加简洁明了。

2.2.3 Bug存在的原因分析

Bug1:登录界面显示密码的“小眼睛”的不稳定。
原因可能是:开发人员粗心大意,前端设计中存在渲染Bug。 Bug2:修改密码后未刷新页面要求重新登录,而是可以继续使用。
原因可能是:开发人员粗心大意,忽略了修改密码后重新登录的业务逻辑;也可能是因为登录依赖于其他软件,导致难以接收到密码修改的反馈而无法实现修改密码后要求重新登录。

2.2.4 产品开发时间预估
任务描述 花费时间/天
需求分析与文档编写 15
数据收集与处理 30
接口开发与测试 35
前端界面设计与开发 30
模型训练与优化 45
系统集成与联调 20
总计 175

产品的开发依赖于模型听语言后转文字,难度较高,语言识别难,在参考其他同类型产品的情况下,也要花费不少开发时间进行定向训练和软件开发,根据上面表格大概可以累计出总时长。

3. 建议和规划

3.1 文心一言

3.1.1 产品规划
3.1.1.1 功能以及NABCD分析

功能设计:在文心一言基础上的视频解析功能。
理由:
视频解析功能旨在利用文心一言的自然语言处理能力,对视频内容进行深度解析,提取关键信息,生成文本摘要或标签,为用户提供便捷的视频内容理解、总结和检索。当前产品已经有良好的问答功能、文件解析功能、图片解析功能以及AI绘图功能,后续考虑视频方面的功能,可以进一步提升软件的泛用性,吸引用户使用。高效性:能够快速提取视频关键信息,节省用户浏览和筛选视频的时间。准确性:利用文心一言的先进算法,能够准确理解视频内容,生成高质量的文本摘要和标签。易用性:功能设计简洁明了,用户无需复杂操作即可享受便捷的视频解析服务。 NABCD分析
Need:随着视频内容的爆炸式增长,用户对于高效、精准地理解和检索视频内容的需求日益强烈。Advantage:文心一言作为自然语言处理工具,能够理解视频内容,生成高质量的文本摘要和标签,提升用户体验;在视频解析方面有着技术上的支持。Benefit:用户能够更快速地获取视频关键信息,节省时间;同时,对于内容创作者和平台方,该功能有助于提升内容传播效率和用户粘性。Competition:当前市场上虽然存在一些视频解析工具,但大多集中在简单的标签生成或字幕识别,缺乏深度理解和语义分析,大语言模型本身对语言有一定的解析作用,适合发展视频理解,能够在技术层面胜过一些竞争对手。Delivery:基于文心一言的视频解析功能具有强大的技术支撑和广泛的应用场景,具备长期发展的潜力。

3.1.1.2 角色配置

开发人员:3名,负责功能开发、系统集成和代码优化。测试人员:1名,负责功能测试、性能测试和用户体验测试。美工设计师:1名,负责界面设计、图标制作和视觉优化。产品经理:1名,负责需求调研、功能规划、接口设计和项目管理。

3.1.1.3 16周的详细计划
周期 任务
第1-2周 需求调研,明确功能定位、制定项目计划、搭建开发环境,准备技术和工具。
第3-4周 完成视频解析功能的接口设计与基础框架搭建、初步进行功能开发、准备测试环境
第5-6周 功能开发并进一步完善、制作初步的界面原型
第7-8周 完成视频解析功能的主体开发、进行功能测试
第9-10周 根据测试结果进行功能优化和bug修复、美工完成界面设计
第11-12周 视频解析功能与界面联调、进行集成测试和性能测试。
第13-14周 进行系统优化和代码清理、撰写用户手册和操作指南
第15周 进行用户体验测试、根据用户反馈进行最后的调整和优化
第16周 发布软件改进版本

3.2 通义听悟

3.2.1 产品规划
3.2.1.1 功能以及NABCD分析

增加社交分享与社区建设功能。用户可以将自己的听单、心得体会、原创音频等内容在“通义听悟”的内置社区中与其他用户交流互动,形成围绕音频内容的线上社群,提高用户使用体验与粘性。NABCD分析

Need:用户在享受音频内容的同时,有着强烈的分享与交流需求。他们希望将自己喜欢的节目、有启发性的观点、或者自己的听后感分享给朋友,甚至参与到更广泛的讨论中。此外,构建社区能够促进用户之间的互动,增加用户粘性,形成独特的文化氛围,进一步提升产品价值。

Approach:通过开发社交分享与社区建设功能,用户可以直接在通义听悟内完成内容分享、话题讨论、用户互动等操作,无需跳转至其他平台。这不仅简化了分享流程,还为用户提供了专属的音频社交空间,有利于形成围绕音频内容的活跃社区。

Benefit:提升用户活跃度与留存率:用户在参与社区讨论、关注他人动态、分享个人见解的过程中,会更加频繁地使用通义听悟,提高产品的使用频次和时长。
增强用户归属感与忠诚度:社区建设有助于形成用户间的情感连接和共同认同,使通义听悟成为用户生活中不可或缺的一部分。
促进内容传播与发现:用户分享的行为有助于优质内容的扩散,吸引更多新用户。同时,社区内的热门话题、用户推荐等也能帮助用户发现更多感兴趣的内容。
收集用户反馈与洞察:社区中的用户讨论和反馈是了解用户需求、优化产品的重要途径,有助于持续改进产品和服务。

Competition:与其他音频类产品相比,拥有社交分享与社区建设功能的通义听悟,不仅提供丰富多元的音频内容,还构建了一个用户深度参与、高度互动的音频社交生态,形成差异化竞争优势。

Delivery:最终,用户将能够在通义听悟平台上轻松分享喜欢的音频内容,参与各类话题讨论,结识志同道合的朋友,享受到集音频消费与社交互动于一体的全新体验。

3.2.1.2 角色配置

开发人员:3名,负责社交分享与社区模块的界面开发、功能开发、系统集成和代码优化。测试人员:1名,负责功能测试、性能测试和用户体验测试。美工设计师:1名,负责社交分享与社区模块的视觉设计和用户体验优化。产品经理:1名,负责需求调研、功能规划、接口设计和项目管理。

3.2.1.3 16周的详细计划
周期 任务
第1-2周 收集用户需求、行业趋势及竞品特点,形成初步功能需求文档、美工开始构思社交分享与社区模块的整体风格与交互设计
第3-4周 讨论技术方案、完成社区功能的接口设计与基础框架搭建、美工具体实现界面样式设计,给出页面原型
第5-8周 进行页面开发、开发用户系统、内容分享、社区管理等后台逻辑。
第9-10周 前后端联调、测试工程师编写测试用例,进行全面的功能测试
第11-12周 开发团队根据测试结果及时修复问题,优化代码
第13-14周 进行系统优化和代码清理、根据测试反馈进行细节调整,提升用户体验。
第15周 完成所有准备工作,正式上线社交分享与社区建设功能
第16周 收集上线后的用户数据,分析功能使用情况,制定后续迭代优化计划

3.3 市场分析

3.3.1 市场概况

大语言模型市场正在经历快速增长的阶段,主要得益于大型数据集的可用性不断提高、深度学习算法的进步、对增强人机通信的需求以及对自动化内容创建和管理的需求不断增长等因素的推动。大语言模型具有广泛的应用场景,被广泛应用于自然语言处理、智能客服、智能助手、智能搜索、数据分析等领域。随着人工智能技术的发展和应用需求的增加,大语言模型市场也在不断扩大。预计未来大语言模型市场将继续保持增长势头,为各行各业带来更多智能化解决方案。大语言模型的用户主要包括企业、研究机构和学生等。随着技术的不断发展和普及,以及大语言模型在更多领域的应用,潜在用户的数量在不断增加。

3.3.2 市场现状

目前市场产品包括:文心一言、ChatGPT、通义千问(与“通义听悟”同源)、讯飞星火等。

1.文心一言

优势:
本土化优势:作为百度推出的产品,文心一言在中文处理上具有较高的准确性和理解力,更适合中文语境下的应用。技术积累:百度在人工智能领域有深厚的技术积累,文心一言在算法优化和模型训练方面具有一定的优势。生态支持:百度拥有庞大的用户基础和丰富的应用场景,可以为文心一言提供强大的生态支持。 劣势:
国际影响力:相比ChatGPT等国际知名产品,文心一言在国际市场的知名度和影响力还有待提升。技术竞争:随着大语言模型领域的快速发展,文心一言需要持续进行技术创新和优化,以保持竞争优势。

2.通义千问

优势:
多语言支持:支持多种语言交互,不仅限于中文,还能应对其他语言用户的查询需求,增强了其在全球市场的适应性和竞争力。技术整合与生态优势:作为阿里云的产品,通义千问能够充分利用阿里集团的技术资源与生态优势,如与阿里巴巴内部其他产品和服务的无缝集成,以及与外部开发者社区的紧密合作,这有助于其快速融入各类应用场景并形成解决方案。 劣势:
市场知名度与用户基础:虽然通义千问具备先进技术实力,但相较于已在全球范围内建立广泛用户基础和品牌认知的竞品(如ChatGPT),可能在市场知名度和用户接纳度上存在一定的差距,需要投入更多资源进行市场推广和用户教育。综合性能对比:根据部分对比分析,通义千问在某些方面(如推理能力)可能与竞品相比存在相对不足。尽管在某些细分领域基于NLP的问答训练表现出色,但总体训练规模或一体化表现可能有待进一步提升。

3.ChatGPT

优势:
技术领先:ChatGPT在算法和模型方面具有较高的技术水平,能够在多种语言环境下提供高质量的回答和对话。全球影响力:作为OpenAI的代表作,ChatGPT在全球范围内拥有广泛的用户群体和影响力。应用创新:ChatGPT在聊天机器人、智能客服等领域有广泛的应用,能够为用户提供便捷的服务体验。 劣势:
数据安全与隐私:随着ChatGPT的应用范围不断扩大,数据安全和隐私保护问题逐渐成为关注焦点。本土化挑战:尽管ChatGPT在多语言处理方面表现出色,但在特定文化和语言环境下,可能仍需进一步优化和调整。

4.讯飞星火

优势:
语音识别与合成技术:讯飞作为语音技术领域的领先企业,讯飞星火在语音识别和合成方面具有较高的技术水平。多模态交互:讯飞星火能够实现语音、文本等多种模态的交互,为用户提供更丰富的交互体验。行业应用:讯飞星火在医疗、教育、智能家居等行业有广泛的应用,能够满足不同行业的需求。 劣势:
技术整合:多模态交互涉及多种技术的整合,讯飞星火需要在技术整合方面持续优化和提升。市场推广:相比其他知名大语言模型产品,讯飞星火在市场推广和品牌建设方面还需加大力度。

上面的产品基本都是竞争关系,目前知名度最大,技术最为前沿的是ChatGPT,市场中ChatGPT占据优势;其余产品业绩大差不差。

领域阶段:这个领域正处于风口阶段,处于迅速上升期。

六、软工实践总结 & 个人技术博客

这个作业属于哪个课程 2302软件工程
这个作业要求在哪里 软件工程实践总结&个人技术博客
这个作业的目标 软件工程实践总结
其他参考文献 《构建之法》

一、五个阶段中的收获

1.需求阶段
需求分析过程中,需要详细确定需求,整理出需求的所有功能点,对各个需求进行数据需求的分析。确定总体项目框架与各个需求的技术点。为后续设计数据库、接口、编码实现理清思路。 2.设计阶段
需要思考编码实现的方便性与部署阶段的可移植性,例如图片的存储,在数据库里可以使用JSON格式存储,并且只保存图片的UUID而不存储地址前缀,以免在数据迁移时产生原数据不可用的问题。接口的设计也需要慎重决定各个字段的名称,以免发生前后端不一致问题,前后端相互传递的数据也需要多加思索,修修改改,一蹴而就并不现实。 3.实现阶段
对各个成员合理分工,按照模块进行划分,安排时间开会,合理规划进度。对springboot框架与vue框架有了进一步的理解。对gitcode的使用熟练度更进一步。 4.测试阶段
学习了使用ApiFox进行单元测试,统一设计用例后一键测试,能够发现不少bug。ApiFox支持设计场景进行测试,测试方便简洁。 5.发布阶段
对docker部署中的打包、生成容器、文件挂载等有了进一步的了解。对nginx代理修改配置文件进一步了解。

二、个人项目/结对编程/团队项目的理解与心得

个人项目
学习了对JSON的处理,学会使用fastjson包下的一些常用API,如parseObject、toJSONString、parseArray等。学会了git的fork、pull、commit等操作,了解git合并项目。 结对项目
掌握了使用vue制作简单的页面,了解axios的原理;学习使用element组件进行页面渲染,从后端获取数据解析后绑定数据到组件。掌握了使用nginx代理vue的dist包,完成前端页面部署,了解了nginx反向代理访问后端。 团队项目
这是一场酣畅淋漓的变成战斗。从数据库设计、接口设计开始便投入了不少时间。了解了数据库的触发器,了解了接口设计的规范。在编码实现中,小程序登录与图片上传花了不少时间,对图片上传存储本地服务器的流程有了进一步的熟识。对一些业务接口的实现也更进一步,mybatisplus的使用更加熟练。测试阶段也发现不少bug,特别是前后端联调之后,总会发现不少问题,不断讨论解决方案解决问题。收获丰富。

三、课程目标的掌握程度

课程目标 掌握程度(100分) 解释
目标1: 理解软件工程师的职业道德规范和实践要求,了解国情社情民情,理解软件产品对社会、健康文化等影响,树立积极向上的软件开发理念。 90 软件开发意义在于方便生活,为人们日常生活带来便捷,实现日常生活中的需求。
目标2: 掌握需求分析的全过程,能辨别客户表述的多样化要求,熟练使用需求表达工具,能够规范、准确地表达客户的需求,构建需求分析模型。 85 能够了解客户的具体需求,将需求整理成文档;掌握使用墨刀等工具进行页面原型的绘制
目标3: 掌握软件开发的全过程,遵循体系结构设计方法和基本设计原则,通过正式的技术评审,完成从体系结构设计模型、数据设计模型和构件级设计模型,形成面向高效可靠的服务组件设计方案或软件系统设计方案。 85 在团队项目中,从需求分析到数据库、接口的设计,循序渐进,遵循瀑布模型,遵守各种设计规则。考量框架构建。
目标4: 能够执行从组件到软件系统的技术评测,具备设计模型的评判能力,具有创新设计意识,能够优选设计方案。 80 创新是一件较难的事情,在项目初期确定各种框架与技术方案,便于后期的编码实现,但在编码开始之前,设计总会有所失误,需要后续进行设计变更。
目标5: 遵循软件开发各阶段文档标准,采用规范的表达,掌握需求规格说明书、系统设计说明书、系统测试报告等文档撰写方法,具备与业界同行交流能力。 90 本次项目中,需求文档、系统设计说明书、系统测试报告等文档均遵守一定的规范,阅读理解简便直观,能与同行进行良好沟通。
目标6: 具有良好的团队意识和合作技能,能够与其他成员开展有效的沟通和协作;能够组织、协调或指挥团队开展工作。 88 能够对一些具体的问题与具体的技术方案提出自己的见解,对团队作出共贡献;能够给出后端代码大体框架,组织后端组员进行业务的开发。
目标7: 能够辨别具体软件项目管理中涉及的构成要素,掌握软件规模和工作量的估算方法,能够选择合适的工具规划软件进度并对项目管理过程进行配置,具备初步的管理复杂软件工程项目的能力。 87 在本次项目中,积极参与项目进度的了解,查看gitcode提交日志;学习了不少gitcode的知识,学习了解决合并冲突的方法,合并后端组代码。

四、五个问题再次思考

寒假作业博客链接

Q1.我都是大学生了,上课还要认真听老师讲课么?

答: 这个问题因课程而论,因繁忙程度而论,并不一定。每个人都有各自必需解决的事情。对于一些必要的课程,老师的讲解总会有老师自己独到的见解,了解前人的经验总结是一件事半功倍的事情。

Q2.如何区分一个好的程序员和不好的程序员呢?

答:与开始时的回答一致。程序员应具有良好的编程能力和思维,能适应各种环境,严谨思考;同时也需要正确地为人处事,明白团队配合的重要性,积极沟通;最后还需要有持续学习的好习惯。

Q3.程序员是否有必要为满足小部分人的需求去做软件,或者为软件添加某些功能?

答:一般情况下,这个问题主要看甲方的需求具体如何,如果极其需要,大致需要遵循甲方需求;如果并不是特别需要,并且对目前项目的进度具有较大的破坏性,应积极与甲方进行沟通,寻求一个双方能接受的折衷方案。

Q4.为什么要在大学中只用一学期软件工程,时间够吗?

答:一学期的时间实际上算是刚刚好,有了一定的项目经验。如果学有余力,再花更长时间进行团队编程,也能提高不少经验,能够有更多团队开发的经验。当然,将时间花在自我修习上也有所提高。

Q5.代码量与个人的编码能力有直接关系吗?

答:个人理解,程序员这一门工作实际上是开卷考试,关键点并非代码量而是针对具体的问题,能够给出该问题的良好的解决方案。代码量和编码能力有一定关系,但并不是成正比。

五、个人技术博客

个人技术博客

这个作业属于哪个课程 2302软件工程
这个作业要求在哪里 软件工程实践总结&个人技术博客
这个作业的目标 软件工程实践总结
其他参考文献 《构建之法》
一、技术概述

目前微信小程序登录通过前端的wx.login方法进行登录,初次登录需要上传头像与昵称。后端需要将前端上传的图片重命名并保存到服务器的文件夹中,并暴露图片访问方式,供前端访问图片。

二、技术详述

1.前端上传头像图片,即将前端生成的临时图片URL地址传递给后端,后端根据文件上传接口接受文件,并编写文件保存方法保存图片。(代码中filePath多余,直接返回fileName,此时前端只收到了fileName,还不是一个可访问的图片;后续在前端调用login方法时,给后端传递数据,将fileName带给后端,后端处理成一个完整的URL返回给前端。)

//uploadPath存的是形如"D:\pictures"这样的路径,当然在部署时需要改成服务器的文件夹路径
@Value("${tomato.upload-path}")
public String uploadPath;
//imgBaseUrl存的是"http://10.133.48.239:8081/img/",公网ip。
@Value("${tomato.img-baseurl}")
public String imgBaseUrl;
@Autowired
UserService userService;

@PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(@RequestParam("files") MultipartFile files) {
        String fileName = null;
        String filePath;
        if (files == null) {
            throw new BaseException("未上传任何文件");
        }
        System.out.println("文件保存地址:" + uploadPath);
        //for (MultipartFile file : files) {
            //调用函数,返回文件存储路径
            fileName = renameFile(files, uploadPath);
            uploadToLocalServer(uploadPath+fileName, files, uploadPath);
            filePath=fileName;
        //}
        System.out.println("返回结果:"+filePath);
        return Result.success(filePath);
    }


123456789101112131415161718192021222324252627

2.renameFile方法的作用是将图片重命名,生成该图片唯一的UUID。

public static String renameFile(MultipartFile file, String uploadPath) {
        //获取 原始文件名
        String originalFileName = file.getOriginalFilename();
        //System.out.println("原始文件名:" + originalFileName);
        //判断文件名是否有值 没有则抛出异常中断程序执行
        if (originalFileName == null) {
            throw new BaseException("未上传任何文件");
        }
        //使用UUID通用唯一识别码 + 后缀名的形式
        //设置唯一文件路径 防止文件名重复 出现覆盖的情况
        String fileName = UUID.randomUUID() + originalFileName.substring(originalFileName.lastIndexOf("."));
        //System.out.println("唯一文件名:" + fileName);
        // 指定文件保存的路径
        String filePath = uploadPath + File.separator + fileName;
        System.out.println("文件成功重命名并获得路径:" + filePath);
        return fileName;
    }


1234567891011121314151617

3.uploadToLocalServer方法是将图片保存到本地文件夹,后续部署阶段只需要修改配置文件使其保存在服务器文件夹。

public static void uploadToLocalServer(String filePath, MultipartFile file, String uploadPath) {
        //根据上传路径创建文件夹File对象
        File saveAddress = new File(uploadPath);
        if (!saveAddress.exists()) {
            saveAddress.mkdirs();// 如果文件夹不存在 创建保存文件对应的文件夹
        }
        // 将上传的文件保存到指定路径
        try {
            file.transferTo(new File(filePath));
            System.out.println("成功");
        } catch (IOException e) {
            throw new BaseException("文件保存失败!");
        }
    }

1234567891011121314

4.在WebMvcConfiguration配置类里添加资源映射;配置该资源映射使前端可以通过”http://10.133.48.239:8081/img/abcdefg.png”这样的路径访问服务器文件夹里的图片。

 /**
     * 设置静态资源映射
     *
     * @param registry
     */
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

        // 使用file:前缀指定文件系统路径
        registry.addResourceHandler("/img/**")
                .addResourceLocations("file:" + filePath );

}

1234567891011121314

5.小程序登录接口,后端点击登录访问该接口后,将数据存储到数据库,并封装返回。

    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    UserService userService;
    @PostMapping("/login")
    @ApiOperation("微信登录")
    public Result login(@RequestBody UserLoginDTO userLoginDTO) {
        User user=userService.wechatLogin(userLoginDTO);
        //生成JWT令牌
        Map<String,Object> claims=new HashMap<>();
        claims.put("userId",user.getId());
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);
        //封装成UserLoginVO对象返回
        //TODO 测试
        UserLoginVO userLoginVO = BeanUtil.copyProperties(user, UserLoginVO.class);
        userLoginVO.setToken(token);
        log.info(userLoginVO.toString());
        return Result.success(userLoginVO);
    }


12345678910111213141516171819

6.userService里的wechatLogin方法,主要思路是拿着appid和secret和前端给的code去访问微信接口,获取用户openId等信息。(详情查看微信小程序官方文档)

//微信服务接口地址
    public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";

    @Autowired
    private WeChatProperties weChatProperties;
    @Value("${tomato.upload-path}")
    public String uploadPath;

    @Override
    public User wechatLogin(UserLoginDTO userLoginDTO) {
        //请求微信官方,获取包含openId等信息的字符串
        String s = getOpenId(userLoginDTO);
        //转成JSON对象,提取字段
        JSONObject jsonObject = JSON.parseObject(s);
        String openId = jsonObject.getString("openid");
        //如果openId不存在,抛出异常
        if (openId == null) {
            throw new BaseException("微信登录openId不存在!");
        }
        User user = lambdaQuery().eq(User::getOpenId, openId).one();
        getOrSetUser(user,userLoginDTO,openId);
        log.info("code="+userLoginDTO.getCode());
        //返回成功登录信息
        User theUser = lambdaQuery().eq(User::getOpenId, openId).one();
        return theUser;
    }


1234567891011121314151617181920212223242526

7.getOpenId方法,获得微信官方接口返回的JSON字符串,解析得到openId。

private String getOpenId(UserLoginDTO userLoginDTO) {
        Map<String, String> map = new HashMap<>();
        map.put("appid", weChatProperties.getAppid());
        map.put("secret", weChatProperties.getSecret());
        map.put("js_code", userLoginDTO.getCode());
        map.put("grant_type", "authorization_code");
        String s = HttpClientUtil.doGet(WX_LOGIN, map);//返回封装的字符串
        return s;
    }

123456789

8.getOrSetUser方法是对数据库进行操作,如果是新用户,就写入数据库,如果是老用户重新登录并上传了新头像和昵称,就需要更新记录。(未解决旧头像的删除问题)数据库里存储的头像只有”uuid.png”,不包括URL前缀。

//返回用户信息或注册新用户
    public void getOrSetUser(User user,UserLoginDTO userLoginDTO,String openId) {
        //如果是新用户,自动注册
        if (user == null) {
            user = new User();
            user.setOpenId(openId);
            //裁剪图片url,获取最后的UUID部分。
            String[] parts = userLoginDTO.getAvatar().split("/");
            String lastPart = parts[parts.length-1];
            user.setAvatar(JSON.toJSONString(lastPart));
            user.setUsername(userLoginDTO.getUserName());
            //这里的是默认初始值,按照需求修改
            user.setGender(1L);
            user.setMajor("无");
            user.setPassword("123456");
            user.setStatus(0L);
            user.setPhone("无");
            save(user);
        }
        //老用户更新头像,忘记写更新昵称了。删除旧图片未实现。
        else {
            user.setUsername(userLoginDTO.getUserName());
            //裁剪图片url,获取最后的UUID部分。
            String[] parts = userLoginDTO.getAvatar().split("/");
            String lastPart = parts[parts.length-1];
            user.setAvatar(JSON.toJSONString(lastPart));
            updateById(user);
        }
    }


1234567891011121314151617181920212223242526272829

关于代码中的jwtProperties和HttpClientUtil,都是一些比较通用的东西,需要自主学习理解(理解后cv大法)。

三、技术使用中遇到的问题和解决过程

1.由于在数据库里存储的图片是JSON格式,后续取出数据需要对数据进行处理,包括转成字符串、拼接前缀使其成为可访问路径等。

//Pictures前拼接URL,参数为article的pictures--JSON
    public List<String> transPictures(String p) {
        List<String> pictures= JSON.parseArray(p,String.class);
        for (int i = 0; i < pictures.size(); i++) {
            pictures.set(i,imgBaseURL+pictures.get(i));
        }
        return pictures;
    }

    //avatar拼接URL,参数为user的avatar字符串--JSON
    public String transAvatar(String a) {
        String avatar=JSON.parseObject(a,String.class);
        avatar=imgBaseURL+avatar;
        return avatar;
    }

123456789101112131415

2.由于upload方法只返回fileName而不是filePath,所以是无法访问的,之所以不在upload时就写入数据库是因为upload是一个通用接口,在文章模块也有图片需要上传,无法确定需要将图片保存在user表还是article表,所以只返回fileName;如果前端需要图片在上传时立刻回显,可以考虑在前端图片加上前缀。而微信登录并不需要图片立刻回显,其会生成一个临时的图片url,不需要使用upload传递的fileName。3.使用JSON格式存储多张图片时,每张图片前后都会有双引号””,需要后端代码中手动裁剪。

四、总结

微信小程序登录只需要参照官方文档,从前端接受code等参数,访问微信接口,取得登录数据。而登录过程中涉及的头像上传,需要程序员自己实现,接收图片下载,回传,需要考虑数据库如何存储图片,如果在数据库里存储url前缀,在数据迁移时会带来问题。

五、参考文献

微信小程序登录

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...