oomall课堂笔记

 一、项目分层结构介绍(传统面向功能)

1.controller层(控制器层)

作用:负责输入输出,接收前端数据,检查输入合法性,把结果返回给前端。

1.处理用户请求,接收用户参数。

2.调用service层处理业务,返回响应。

2.service层(业务逻辑层)

作用:实现系统的功能(业务)。

1.封装业务逻辑(业务代码一般写在service层)。

2.调用dao层或mapper层处理数据,为controller层提供服务,起承上启下的重要作用。

3.dao层(数据访问层)

作用:定义了对数据库增、删、改、查的操作。

1.定义了对数据库的基本操作(增删改查),不包含业务逻辑。

2.调用Mapper层接口实现对数据库的操作。

4.mapper层

主要职责:执行SQL语句,与数据库交互

1.将Java方法与SQL语句进行映射,执行SQL语句。

2.负责对象模型与关系模型的转换。

5.调用逻辑

用户通过前端发送请求到controller层,controller层调用service层,service层调用dao层,dao层调用mapper层,最后mapper层对数据库进行操作,返回数据。

二、oomall面向对象体系结构介绍

在传统的面向功能的体系架构中,service层的代码是功能性的,BO只作为值对象在不同层之间进行传递(只有get和set方法,无具体业务逻辑)。

而OOMALL是六边形体系结构,沿用了mvc体系结构,但更强调领域模型,将尽可能多的业务逻辑放在BO(Business Object 业务对象)中,让BO成功系统的核心。

这么做的目的是:实现系统的高内聚和低耦合,提高可读性(容易理解)和可维护性,同时也提高了重用性。

将业务代码放在BO中,使得业务逻辑更加集中,实现高内聚。同时避免了将业务代码分散在各层中,降低了与其它层的耦合度,实现低耦合。同时业务逻辑的聚焦可以提高代码的可读性和可维护性(更方便找到和修改相关业务逻辑)。

解释一下:原本面向功能的业务代码是全放在service层,会导致的问题是,写的代码行数很多很庞杂,看起来头晕,在动辄几千行的业务代码里,想找到相关业务的代码很不容易。

现在把业务逻辑放到BO中,根据信息专家的设计思想:将具体的业务逻辑和数据封装在具有最多相关信息的对象中。这样一来相关的业务逻辑会被放在与其相关性最高的BO中,而BO往往有多个,各个BO各司其职,想要找到对应的业务代码更有针对性,只需要去相应的对象中找。

所谓“满血模型”就是:领域驱动设计中的充血模型。充血模型是指将数据和对应的业务逻辑封装到同一个类中。将service层变成协调者,将service中的一个方法分解成BO中若干个方法。实现高耦合和低内聚,提高代码的可读性、可维护性和可重用性。

三、软件设计的七大原则

1.开闭原则

开闭原则的核心思想是:对扩展开放,对修改封闭

意思是:在不修改现有代码的前提下(对修改封闭),实现对功能的扩展(对扩展开放)

具体的操作是:通过抽象方法(比如创建一个抽象接口,把所有接口统一到该接口上),将变化的部分抽象化。当需要扩展系统功能时,只需要增加新的实现类(不会动到已有的代码)。

一般是在可能的变化点和演进点使用开闭原则。

2.Liskov可替换原则

如果子类在继承父类之后,仍然具备父类中的性质,则满足Liskov可替换原则。

需要注意的是,Liskov可替换原则仅要求子类继承父类时不能修改父类的性质(这意味着去overwrite父类的方法时,不能随意去重写,必须确保父类的性质在子类中依然成立),子类仍可以加入自己的性质。

满足Liskov替换原则必然满足开闭原则

3.依赖倒置原则【爱考】

设计中如果不直接调用具体的实现类,只调用接口满足依赖倒置原则。

满足依赖倒置原则必然满足Liskov可替换原则因为依赖倒置是面向接口编程,而接口里只定义了方法,没有实现类,因此子类继承父类接口,能够保证父类的性质在子类中仍旧存在。

高层模块不依赖低层模块,两者都依赖其抽象;抽象不依赖细节,而细节依赖抽象。这就是依赖倒置。

4.单一职责原则

一个类有且仅有一个引起它变化的原因,否则类应该被拆分。

职责:每一个业务的方法,把职责分配给一个对象。

5.接口隔离原则

客户端不应该被迫依赖于它不使用的方法。(人话说:接口不能对应有它不需要的方法,要把接口拆分为多个小接口,然后逐个依赖,直到形成原子性的,完美的配对)

6.迪米特原则

不跟“陌生人”说话,只与你的直接朋友交谈。

如果两个软件实体无需直接通信,就不应该发生直接的相互调用,可以通过第三方转发该调用。

7.合成复用原则

优先考虑组合或聚合等关联关系来实现,其次才考虑继承关系实现。

四、GRASP 方法

1. 创建者

创建者回答的问题是:应该由谁来负责创建类的实例

如果满足下面几种情况,则由对象B创建对象A

1. 如果B包含A(B为整体,A为局部,比如B是福建省,A是厦门市,则福建省B创建厦门市A)。

2. B记录A 或 B频繁使用A 或者 B有A的初始化数据 又或者 B使用A 等等...

我们称类B是类A对象的创建者。

2. 多态

多态是指当相关选择或行为类型(类)变化而变化时,用多态操作为行为变化的类型分配职责(这句话要仔细品味,十分精准说明了多态)。

解释一下:比如我们可以定义一个接口,让不同的子类以自己独特的方式去实现这个接口。接口负责定义功能,子类负责功能的具体实现。当需要改变功能时,我们只需要切换子类就可以提供不同的功能。

“相关选择或行为类型变化而变化”可以这么理解:接口的功能会随子类的不同而不同,这里指的是选择不同的子类(类型)能提供不同的功能(行为)。所以行为指的是功能,类型指的是具体实现功能的子类。

“用多态操作为行为变化的类型分配职责”的意思是:用多态操作为实现功能的子类(行为变化的类型)分配职责。

使用多态的一些场景:比如在一个接口或者类下面定义了很多实现类,实现类用于具体实现接口或类的功能。这样的话实现类会满足Liskov可替换原则,因为任意切换实现类都不会影响父类的功能(比如都是计算折扣或者计算优惠)。

3. 信息专家

问题:分配职责给对象的基本原则是什么?

“知者为之”谁具备完成职责所需的信息,就由谁来承担职责。

比如:api的分配

4. 高内聚

高内聚通俗的说是指:相关的职责和操作被尽可能聚集在一起,使得职责尽可能单一,功能高度集中。

比如一个订单数据存取类(OrderDAO),订单既可以保存为Excel模式,也可以保存为数据库模式;那么,不同的职责由不同的类来实现(比如A类只负责Excel模式的保存,B类只负责数据库的保存),每个类的职责尽可能单一,以特定目的被组织成强关联形式,这样才是高内聚的设计。

而在一个低内聚的类中,会执行很多不相关的操作(比如一个类既处理Excel模式的保存,也处理数据库模式的保存),这将使得系统变得难以理解、重用和维护。

5. 低耦合

低耦合通俗的说是指:系统各个元素间的连接或依赖关系弱。

好处是:其中一个元素的改变不会影响其它元素。使得系统更好理解和维护。

那上面的例子说把订单保存分成Excel模式和数据库模式两个类,类与类之间的依赖关系弱,是低耦合的。类内部职责单一,功能集中,是高耦合的。因此高内聚和低耦合往往是同时存在的。

6. 中介(间接)

分配职责给中间对象以协调组件或服务之间的操作,使得它们不直接耦合。中间对象是在其它组件之间建立的中介。

目的是:避免多个事务之间的直接耦合,解耦对象降低耦合度,提高系统的重用性。

使用场景:比如A类通过一个中间类来使用另一个B类,这样B类的变化不会对A类产生影响,这样满足了开闭原则。也有可能是中间类发生变化但不影响A类。

7. 纯虚构(虚拟)

创建一个不代表真实世界概念的类,给这个类分配一些高内聚的职责,该类并不代表领域模式的概念,而代表虚构出来的事物。

将一组高内聚的职责分配给一个虚构的或处理方便的“行为”类,将一部分类的职责转移到纯虚构类中。

比如将商品的优惠活动(领域对象中有的)分成优惠折扣和优惠条件(虚构出来的)两个部分,实现低耦合和高内聚的目标。

8.控制器

把接收或者处理系统事件消息的职责分配给一个类,该类不包含太多业务逻辑。

比如controller层的类可以将用户界面提交的请求转发给其它类来处理,就是用了控制器方法。

9.受保护变化

找出可能有变化或不稳定的元素,为其创建稳定的“接口”二分配职责。

使得这些元素中的变化或不稳定的点不会对其他元素产生不利影响。

10.总结一般是:

1.多态的使用能满足Liskov可替换原则,然后能实现高内聚和低耦合。

2.虚拟和间接的使用能实现开闭原则,然后能实现受保护变化。

五、设计模式

1. 模板方法设计模式

模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或总行为的方法。

基本方法是实现算法各个步骤的方法。

写在父类中的填空题,子类不同会给空填上不同内容。

2. 策略模式

定义一系列算法,把它们封装在对象内部,并且可以任意替换。

最好在变化点使用,为了将系统和变化点松耦合,将变化点从代码中提出。用多态方式实现不同的子类,以便日后增加新算法。

老师例子:shop模块的打包算法。用策略模式把会变化的点抽离出来,用了低耦合和多态,让其满足依赖倒置原则和Liskov可替换原则,使得算法部分满足面向对象的终极目标开闭原则。

3. 桥接器模式

将问题分解成独立的两个部分,任意组合形成最后的解决方案。

五、dto、vo、bo、po介绍

领域模型颜色的具体含义:

白色存在mysql数据库,蓝色表示从其它部分拿到的,黄色表示存在mongo数据库,绿色部分是全用java写的代码。

dto(data transfer object)数据传输对象:【controller层】

用于服务层之间数据传输。

包含数据对象,主要是变量定义和get、set方法。

dto可以封装需要传输的数据。

vo(view object)视图对象:【controller层】

用于controller层和前端数据传输。

同样包含数据对象,主要是变量定义和get、set方法。

在controller层将数据传递给前端展示。

bo(business object)业务对象:【dao层】

封装业务逻辑中的数据。

包含业务逻辑。

包含业务逻辑的数据以及与这些数据相关的业务方法。

po(persistent object)持久化对象:【mapper层】

用于表示数据库中的一条记录,与数据库表结构对应

po通常与数据库表中字段一一对应

一般用在mapper层或dao层与数据库交互

oomall项目特点:包含业务逻辑的bo对象写在了dao层中,称之为满血模型。

六、测试心得

在oomall项目中,测试主要分为3类:切片测试,集成测试,性能测试。

切片测试:只关注程序的局部(小到比如一个方法;再大一点可能是一个层,比如service层;最大不超过一个模块,比如顾客模块),验证功能或特性能否按需求工作。

集成测试:验证多个模块或组件(小到比如顾客+订单模块,大到整个oomall项目)集成后能否在一起正确工作。

性能测试:测试程序的性能(并发、响应速度...)能否满足要求。

1.测试测的是测试方法中有用到的类,覆盖率看的是进入了多少方法,执行了多少代码

2.覆盖率看的是占比,提高覆盖率方法有2,1.删除无关的变量,get set方法等,甚至注解。2.编写更多测试用例。

测试语法:

 1.这行代码使用Mockito框架模拟了一个条件。当调用redisUtil这个模拟对象的hasKey方法,并且传入任意的字符串时,模拟返回结果是false

Mockito.when(redisUtil.set(Mockito.anyString(), Mockito.any(),Mockito.anyLong())).thenReturn(true);

知识点1:Mockito.anyString()用于匹配字符型参数的值。Mockito.any()用于匹配任何参数的类型和值。Mockito.anyLong()用于匹配长整型型参数的值。

知识点2:.thenReturn(true)是Mockito语法的一部分,它定义了当匹配到模拟条件时应该返回的值。

2.这里定义了一个JSON格式的字符串,它代表了创建客户时传入的数据,包含用户名、密码、名字和手机号。

String body = "{\"userName\":\"699275\",\"password\":\"123456\",\"name\":\"test\",\"mobile\":\"12345678900\"}";

知识点3:反斜杠(\)是转义字符,用于允许引号字符(")出现在字符串字面量中。在JSON中,所有的字符串都需要用双引号包围,而Java字符串字面量也以双引号定义,所以需要用反斜杠来转义内部的双引号,以防止它们被解释为字符串的终结符。

3.使用MockMvc对象执行一个HTTP POST请求。/customer代表了请求的URL。

this.mockMvc.perform(MockMvcRequestBuilders.post("/customer");

知识点4:this.mockMvc.perform是Spring MVC测试框架的一部分,用于执行一个模拟的HTTP请求。

知识点5:MockMvcRequestBuilders.post是用来构建一个HTTP POST请求的方法。

4.设置一个预期条件,使用JSON路径表达式检查返回的JSON对象中errno字段的值是否与ReturnNo.CUSTOMER_NAMEEXIST.getErrNo()返回的值相同。

.andExpect(MockMvcResultMatchers.jsonPath("$.errno",is(ReturnNo.CUSTOMER_NAMEEXIST.getErrNo())))

 MockMvcResultMatchers.content()用于验证HTTP响应的内容。测试方法中使用.andExpect结合MockMvcResultMatchers来声明对响应的期望,如果响应不符合这些期望,测试将会失败。

知识点6:.content中用于装之前定义String类型的body中的字节流,会以方法体的形式传输。

.content(body.getBytes(StandardCharsets.UTF_8))

测试样例如下:

@Test
    public void updateCustomerById1() throws Exception{
        Mockito.when(redisUtil.hasKey(Mockito.anyString())).thenReturn(false);
        Mockito.when(redisUtil.set(Mockito.anyString(), Mockito.any(), Mockito.anyLong())).thenReturn(true);
        String body = "{\"name\":\"赵永波\",\"mobile\":\"13159235541\"}";
        this.mockMvc.perform(MockMvcRequestBuilders.post("/customers")
                        .header("authorization", customerToken)
                        .content(body.getBytes(StandardCharsets.UTF_8))
                        .contentType(MediaType.APPLICATION_JSON_VALUE))
                .andExpect(MockMvcResultMatchers.content().contentType("application/json;charset=UTF-8"))
                .andDo(MockMvcResultHandlers.print())
                .andReturn().getResponse().getContentAsString();
    }

七、常用注解和redis语句

常用注解:

1.@PathVariable用于将URL中的变量绑定到控制器处理方法的参数上。

2.@RequestParam用于将请求参数映射到控制器方法的参数上。

3. @Repository注解表明这个接口是一个Spring Data Repository,即一个持久层的抽象,用于访问数据源中的数据。

4. @RestController是一个特殊的控制器注解,表明这个类是一个控制器,并且返回的数据都将自动被转换为JSON或XML等格式,并写入HttpResponse中。

5.@RequestMapping用于定义请求和控制器方法之间的映射关系,可以作用在类或方法上,指定请求的URL、HTTP方法、请求参数、头部等信息。可以将一个HTTP GET请求映射到一个特定的处理方法。

6.@Autowired用于自动注入Spring容器管理的bean。当在字段、构造器、方法上使用@Autowired时,Spring将会在容器中查找并自动提供相匹配的bean实例。‘’

7.@GetMapping用于处理HTTP的GET请求,对应一个处理GET请求的方法。

redis语句:

redisUtil.hasKey(参数):检查Redis中是否存在指定的键,返回一个布尔值表示是否存在。

redisUtil.decr(参数):对指定的键对应的值进行减1操作,并返回减1后的值。

redisUtil.set(参数):设置指定的键值对到Redis中,可以设置键的过期时间。

redisUtil.get(参数):从Redis中获取指定键对应的值。

redisUtil.del(参数):从Redis中删除指定的键。

八、数据库与实体类心得

 Spring Data JPA数据库访问接口心得:

 CustomerPoMapper接口继承自JpaRepository,这意味着继承了一系列的标准CRUD操作,并且可以通过声明方法名的方式来自定义查询。

findBy告诉Spring Data JPA这是一个查询方法。

UserName是实体类CustomerPo的一个属性(这里假设有userName字段),Spring Data JPA会按照这个字段进行查找。

方法参数String userName是查询条件的值。

最终生成的SQL大致为SELECT * FROM customer_po WHERE user_name = ?。

findByUserNameAndMobile(String userName, String mobile):

findBy同上。

UserNameAndMobile表示要根据userName和mobile两个字段进行查询。

方法的两个参数分别对应这两个字段的查询值。

最终生成的SQL大致为SELECT * FROM customer_po WHERE user_name = ? AND mobile = ?。

数据库与实体类命名心得:

Java实体类中,通常采用驼峰命名法(CamelCase),其中每个单词的首字母大写,且没有下划线。例如,creatorId和creatorName。

在数据库中,下划线命名法(snake_case),其中单词之间使用下划线分隔,例如 creator_id 和 creator_name。

原因是:大多数数据库对大小写不敏感,下划线命名可以提高数据库的可读性。

可以通过注解来指定实体类属性(Java中的字段)和数据库表列之间的映射关系。

 @Column(name = "creator_id") // 指定数据库列名为 creator_id
 private Long creatorId;

导入数据库和表的技巧:

1.打开mysql

2.source .sql文件

非空处理心得:

Optional<CustomerPo> 是Java中的一个返回值类型,其中 Optional 是一个容器对象,用于包含非空对象。使用 Optional<CustomerPo> 作为返回值意味着当你调用一个返回此类型的方法时,你将得到一个 Optional 对象。调用者可以使用诸如 isPresent(), isEmpty(), get(), orElse() 等方法来检查值是否存在,并且在不同的情况下执行相应的操作。 get()方法是获取Optional容器里面的CustomerPo对象。

cloneFactory心得:

1.删掉@Data注解

2.实体类手写get set方法

3.@CopyFrom(CustomerPo.class)

九、dto、vo、bo、po心得

 

发现如下几条规律:

1.在属性的完整性和规模上看:PO>BO>DTO>VO(Po对象含有dto、vo、bo对象的几乎所有属性。规模最大。)

2.推测后面新增的属性服务于业务的需要。

十、其它学习心得

— 面向对象一定要新建对象。

— region模块的集成测试可以作为案例进行学习。

— 用mysql 10秒400个请求已经严重阻塞,用redis进行缓存第1次速度会慢,后面会快。bo、po理论上都可以存,但redis一般只存bo,而且是存贫血bo,仅仅存属性,关系也不存,存dto是最快的,但不能存dto,因为属性改名了不能及时反应。

— 蓝色是调其它模块的api,黄色是mongodb,白色是mysql,绿色是redis。

读代码心得:

1.看层次,由外向内,controller->service->dao->mapper

2.看传入的参数类型

3.看返回参数的类型

4.看调用内一层的方法

5.看业务逻辑

画时序图心得:

1.生命线框的长度要注意,service层生命线框最长,要等所有后续步骤执行完才能结束。

2.方框里customer:Customer,前者是对象,后者是实体类。

3.箭头上写的是后面一层中的方法,意思是前者调用后者中的方法。 

 十一、region模块

4.1 使用模式:

创建者+信息专家

4.2 知识点:

— 缓存只在service和dao对象中做,bo对象不做缓存。

— 缓存不存相同东西。

— 动态模型是面向对象的血和肉,动态模型是顺序图,静态模型是类图。

— exception分支不会测到,所有分支都要测到。从功能角度去测试。

4.3 阅读源码:

— 1.如何创建对象。2.如何增和查(createregion,findbyid)。3.看dao层代码如何缓存(regionservice,region(getparentregion,getancestor,changestatus))。

— 要看javaee缓存的知识

4.4 模块结构:

controller【dtp】【vo】

dao【bo】

mapper【po】

4.5 流程图分析:

1、2、3、8是面向功能的,因为此时还没有对象。

service层职责分配,如何把要实现的功能交给不同部分去做。service外面向功能,service内面向对象。增删改等第1步要做的是变出对象。

以后只画从service层开始到dao层结束。

dao层提供对象。

loop循环10代表10级行政区,

十二、payment模块

4.1 使用模式:

工厂方法+适配器模式

4.2 知识点:

— 做判断时机的标准是,如果当前已有信息足够进行判断,则进行判断。

— 如果报红要编译一下compile

— redis的操作是在core/mapper/redisUtil下

— openfeign借用了controller中所有的注解,定义了自身去调其它平台的接口,用restful风格。

— controller是定义了别人来调我们应该提供一个什么样的restful风格。

— 多态:共性的放在父类里,个性的放在子类里。(有子类的类是父类,父类有子类共性的属性)。

— 值对象:不是满血对象,只有值。

4.3 阅读源码:

— 1.阅读一下登录授权的代码,关注AOP。2.dao层channeldao的代码。

— 要看javaee微服务的知识。

4.4 模块结构:

config:

controller:【dto】【vo】

dao:【bo】【channel】

mapper:【generator】【manual】【openfeign】

service:

channel是支付渠道。

generator是自动生成的代码,mannual是自己写的代码。

openfeign(基于restful的api)是外部接口。

4.5 流程图分析:

第3步创建者,第6步adaptor是一个适配器接口,满足间接和多态,满足可替换达成开闭,遵循依赖倒置,不满足接口隔离(所有接口合在一起),第5步取出的是满血对象。

十三、shop模块

4.1 使用模式:

策略模式+桥接器模式

4.2 知识点:

— 当要求插入数据不重复时,可以直接利用mysql的唯一索引的特性。

— 商铺和模板原本是一对多,现在对象模型变成一对一,只记默认模板计算运费。

— 父类白色,子类黄色,把对象一劈两半,一般存mysql,一半存mongo,拿到运费模板要看到底下所有地区,nosql只支持主键查询功能,不能直接看到运费模板下所有主键,要在mysql中记录nosql中所有的主键,索性把共用属性也放到mysql中。nosql的好处是能将数据量搞到最大。

— 订单模块订单的数量级很大,所以在订单要用mongo,在大数量的前提下,用mongo的查询和插入速度会快。

4.3 阅读源码:

1.RegionTemplateDao中的insert函数。weightTemplateDao的insert函数。

4.4 模块结构:

config

controller【dto】【vo】:

dao【bo】【openfeign】【template】

mapper【openfeign】【po】

service【listener】

service中listener中存放的是MQ的内容。bo对象分为两个包,白色在根目录底下,黄色在template底下,绿色在divide底下;和mongo有关的放在template下,无关的放在根目录下。openfeign因为有调region模块,写在mapper下。

4.5 流程图分析:

用了多态的方法去支持两种模板类型,分为运费模板和重量模板,定义了虚拟的接口:

十四、product模块

4.1 知识点:

— 打折优惠活动是变化点和演进点。

— 缓存了Category、Product、Onsale、各类Activity以及它们的关系。

4.2 流程图分析:

对1来说,设置了一个OnSaleExecutor接口,使得调用Product和OnSale时通过接口来访问,满足依赖倒置原则,进而满足Liskov可替换原则,进而满足开闭原则。Excutor不同,会得到不同的Onsale对象。

对2来说,ProductFactory是从ProductDao中的build方法抽离出来,形成一个独立的抽象类,对于底下不同的Excutor,在做build的时候,放入的excute不一样,做成不同的子类,build出不同的executor。运用了多态的设计模式。

运用了工厂方法的设计模式,SpecOnSaleProductFactory和ValidSaleProductFactory和NoOnSaleProductFactory是工程,然后SpecOnSaleExecutor和ValidOnSaleExecutor和NoOnSaleExecutor是产品。

十五、期末复习

15.1 类图绘制复习:

1.常用的是关联和依赖关系。对依赖关系来说箭头的起点是依赖方,箭头的终点是被依赖方,所以是始依赖终。比如service依赖payTrans是因为service要根据 payTrans 的 status 属性判断是否分账。

2.调用工厂方法制造的要画出工厂类。PayAdaptor payAdaptor = this.factory.createPayAdaptor(shopChannel);
是依赖关系,被创建的对象依赖工厂。
3.在if语句中调用的方法也要写上去,有get方法说明一定有相关属性。
4.像在开头声明的这些类统统和service是关联关系
只有关联关系要在连线上写对应变量关系。
5.像下面这些生成的变量全部和service是依赖关系
如果是dao或者factory调用了某个方法生成了某个对象,与生成对象间是依赖关系
如果是非dao或factory调用了get方法获得了某个对象,是关联关系。
6.如果可以通过new来创建自身,说明带有create方法:
7.类和方法的来源可以从以下几个地方找(保证不重不遗):1.类开头定义的变量。2.生成的对象(包括new)。3.有方法可以调用的对象。4.判断语句里出现的对象。
15.2 时序图绘制复习
@Service
public class RefundService {
 private DivRefundTransDao divRefundTransDao;
 private ShopChannelDao shopChannelDao;
 private PayTransDao payTransDao;
 private RefundTransDao refundTransDao;
 private PayAdaptorFactory factory;
 …
 @Transactional
public RefundTransDto createRefund(Long shopId, Long paymentId, Long amount, long divAmount, 
UserDto user) {
 //获得支付交易
 PayTrans payTrans = this.payTransDao.findById(paymentId);
 //获得支付交易的商铺渠道属性
 ShopChannel shopChannel = payTrans.getShopChannel();
 //通过商铺渠道的商铺属性判断支付交易是否是同一商铺,
 if (PLATFORM != shopId && shopId != shopChannel.getShopId()) {
 throw new BusinessException(ReturnNo.RESOURCE_ID_OUTSCOPE, 
String.format(ReturnNo.RESOURCE_ID_OUTSCOPE.getMessage(),
"退款对应的支付交易", paymentId, shopId));
 }
 //创建退款交易
 RefundTrans refundTrans = new RefundTrans(shopChannel, payTrans, amount, divAmount);
 this.refundTransDao.insert(refundTrans, user);
 PayAdaptor payAdaptor = this.factory.createPayAdaptor(shopChannel);
 //根据 payTrans 的 status 属性判断是否分账
 if (PayTrans.DIV == payTrans.getStatus()) {
 //获得支付交易的分账属性
DivPayTrans divPayTrans = payTrans.getDivTrans();
 //需要先调用分账回退 API
 DivRefundTrans divRefundTrans = new DivRefundTrans(refundTrans, divPayTrans, shopChannel);
 this.divRefundTransDao.insert(divRefundTrans, user);
 this.payAdaptor.createDivRefund(refundTrans, divRefundTrans);
 }
this.payAdaptor.createRefund(refundTrans);
 RefundTransDto dto = this.getDto(refundTrans, shopChannel);
 return dto;
}
 …
}

注意:1.写最上面框里的文字时,字体要小,否则试卷可能写不下。2.生命线箱(Lifeline box)在service上要足够长,一般延伸到结束。3.消息要从生命线箱(Lifeline box)上引出。4.构造对象要用create方法,格式:create+类名(参数)。

1.obj到service看的是代码返回的对象,对象的创建如果是调用类的get方法就用create+类名。

2.注意最顶上的方框:payTransDao:PayTransDao,:号前面的是对象,后面的是类。

3.对于if判断和异常抛出的画法需要学习:

注意是从service层抛出异常。

15.3 活动图绘制复习

活动图的基本组成:动作结点、判断结点、分支、合并结点、开始和结束结点等。

是用于描述业务流程和代码的逻辑关系。

请注意每个动作结点用圆角矩形来表示,判断结点是菱形。

15.4 状态机图绘制复习

状态机图的基本组成:状态、状态的迁移、初始状态符号和终结状态符号。

作用:可用于描述对象的状态以及状态的迁移。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值