[实习记录]从底层到上层开发第一个接口(上篇)

达芬奇密码2018-08-13 09:58

1      需求分析

用户填写个人信息时,需要通过下拉列表选择省份和城市。这个功能可以前端通过js从某个源获取。这种方法存在一个问题,当省份或城市的名称有变动时,会造成不一致的情况。针对这一问题,解决方案是——通过底层提供的web服务实现此功能。其中,用户选择的省份和城市均用code(区域编号)存储,根据code显示其名称。如此以来,当城市名称变动,只需要再数据库底层修改对应数据即可,对scratch服务没有影响。

所以,“大概”需要两个接口……

1.       获取所有省份列表,省份信息包括[省份code][省份名称]

2.       获取某省份下的所有城市列表,城市信息包括[城市code][城市名称]

为什么说“大概”?其实这种口头的接口描述是不靠谱的。编程的正确顺序是——自顶向下设计,自底向上实现。所以要实现某一功能接口,首先要和前端工程师进行接口约定。

前端新建两个接口。

设置请求和相应信息。

好了,可以放心地编程了。

2      POJO及项目架构介绍

2.1     Meta/DTO/BO/VO介绍

Meta

定义: DB一一对应的JAVA类型

使用场景: 主要在 dubbo-provider 内部使用

 

DTO

定义: 和基础服务逻辑对应的JAVA对象,主要表现为持有Meta里部分或全部字段,并在此基础上增加业务需要的字段(可以是基本类型,也可以是其他DTO类型)

使用场景: 主要在 dubbo-api 里定义并使用

备注: 由于分层概念,Meta是不暴露给外层调用者的,所以禁止DTO直接继承Meta

 

BO

定义: 和组合服务逻辑对应的JAVA对象,主要表现为持有DTO里部分或全部字段,并在此基础上增加业务需要的字段(可以是基本类型,也可以是其他VO类型)

使用场景: 主要在 manager层 里定义并使用

备注: 由于分层概念,禁止BO直接继承DTO

 

VO:

定义: Web层对应的JAVA对象

2.2     项目架构解析


在上图所示的分层模型中,Service层及依赖的DB层之间相互隔离,以便进行拆分。Service层提供基础服务,其API暴露给外界,提供RPC远程调用。Manger为组合服务,根据业务逻辑可能调用多个Service。同样,Controller也可能调用多个Manager。这种分层架构方便解耦,易于业务拆分和扩展。

3      编码实现

3.1     数据持久化层


3.1.1    定义meta

@Data
@AnnonOfClass(desc="meta
", tableName="meta")
public class Meta implements Serializable {
    private static final long serialVersionUID = -3954012542541127145L;
    /**
     *
编号.
     */
   
@AnnonOfField(desc = "code",primary = true, dbFieldName = "code")
    private Long code;

    @AnnonOfField(desc = "dbUpdateTime", dbFieldName = "db_update_time", dbUpdateTime = true)
    private Date dbUpdateTime;

    public static void main(String[] args) {
        String createSQL = SqlScriptUtil.genCreateSql(new Meta());
        System.out.println(createSQL);
    }
}

其中,@AnnonOfField的用法请参考print-dao-3.2.1.jar包中的doc文件。具体位置通过maven仓库查找。

运行Main函数,可以看到打印出的sql语句,可以据此建立数据表。

3.1.2    定义dao

public interface Dao {
   
List<City> listByCode(Long code);
}

没啥可说的。

3.1.3    实现dao

Repository("cityDao")
public class DaoImpl extends PolicyObjectDaoSqlBaseOfAutowired<Meta> implements Dao {
    @Override
    public List<City> listByCode(Long code) {
        DBCondition dbCondition = new DBCondition();
        dbCondition.addWhereByValue("code", code);
        DDBParam ddbParam = new DDBParam();
        ddbParam.setOrderColumn("order_value");
        return PrintDaoUtil.queryObjectsWithPrepare(this, dbCondition, ddbParam);
    }
}

SQL组件PrintDaoUtil同样参考上述jar包中的doc文件。

3.2     Service


3.2.1    定义DTO

DTO为例。

public class DTO implements Serializable{
    private static final long serialVersionUID = 3837950491998754478L;
   
private Long code;

    public Long code() {
        return code;
    }

    public void setCode(Long code) {
        this.code = code;
    }
}

没啥说的,注意实现Serializable

3.2.2    定义Service接口

public interface SomeService {
    /**
     * @param code.
     * @return
    
* @throws MiscServiceException
     */
   
List<DTO> queryMetaByCode(Long code) throws MiscServiceException;
}

3.2.3    实现Service

SomeServiceImpl.java


@Service("someService")
public class SomeServiceImpl implements SomeService {
    @Resource
    private Dao dao;

    @Override
    public List<DTO> queryMetaByCode (Long code) throws MiscServiceException {
        if(code == null) throw new MiscServiceException("code
参数为空", MiscErrorCode.MISS_PARAM);
        List<Meta> metaList = cityDao. listByCode (code);
        if(metaList == null || metaList.isEmpty()) throw new MiscServiceException(("
查询结果为空"), MiscErrorCode.CITY_NOT_FOUND);
        return ConvertUtils.convertList(metaList, DTO.class);
    }
}

3.3     Manager


3.3.1    定义BO

BO为例。
@Data
public class BO implements Serializable {
    private static final long serialVersionUID = -5138268045097578727L;
   
private Long code;
}

3.3.2    定义Manager

Manager.java

@Component
public class Manager {
    @Resource
    private SomeService someService;


   
public List<BO> queryMetaByCode(Long code) throws MiscServiceException {
        return ConvertUtils.convertList(someService. queryMetaByCode(code), BO.class);
    }
}

3.4     Controller


3.4.1    定义VO

VO为例。
@Data
public class VO implements Serializable {
    private static final long serialVersionUID = 6575346609598828802L;
   
private Long code;

}

3.4.2    定义ConvertUtils

下文详述。

3.4.3    定义Controller

Controller.java


@Slf4j
@RestController
@RequestMapping("/path")
public class Controller {
    @Resource
    private Manager manager;

    @RequestMapping("/get")
    public ResponseEntity get (@RequestParam(value = "code") Long code) {
        try {
            return ResponseView.success(AreaConvertUtils.convertCityBOVO(manager. queryMetaByCode (code)));
        } catch (MiscServiceException e) {
            log.error(e.getErrorLog());
            return ResponseView.fail(e.getErrorCode(), e.getErrorMessage());
        }
    }
}

至此,自底向上基本流程完成。

4      测试

4.1     单元测试

单元测试通常测试Service层至底层。

在要测试的Service实现类点击Ctrl+Enter,选择Create Test,会自动地在test目录相应位置生成测试类。

编写测试类如下:
public class SomeServiceImplTest extends BaseTest {
    @Resource
    private SomeService someService;

    @Test
    public void queryMetaByCode () throws Exception {
        Long provinceCode = 130000L;
        List<DTO> dtoList = someService. queryMetaByCode (code);
//        dtoList.forEach(dto -> {
//            System.out.println(dto.getCode());
//        });
        assertTrue(!dtoList.isEmpty());
    }
}

此处要注意的是,使用断言而不是打印,目的是方便项目构建时自动进行测试。

然后点击绿色箭头进行执行。

可以通过进度条看到运行结果。

4.2     集成测试

新建如下配置进行maven构建。

新建如下配置运行工程。

执行后,通过http请求访问该接口。

上图是执行成功的情况。如果执行不成功,请检查:

1. 是否开启了VPN。如未启动,参考http://ks.netease.com/blog?id=7829

1、是否同步了最新代码。如未同步,参考下一节。

5      提交代码

首先使用如下命令,将本地分支和远程分支建立关联。

$ git branch --set-upstream feature_2017_07_06 origin/feature_2017_07_06

之后进行pullpush时就无需带参数。

5.1     使用git Bash

分别使用如下4个命令。

Git stash 缓存当前空间

Git pull拉取远程最新分支

Git stash pop还原缓存的最后视图,与当前分支合并

Git push 提交更改

具体如下:

gezhicheng@HIH-D-9030 MINGW64 /d/WorkSpaces/Intellij/scratch (feature_2017_07_06)

$ git stash

No local changes to save

 

gezhicheng@HIH-D-9030 MINGW64 /d/WorkSpaces/Intellij/scratch (feature_2017_07_06)

$ git pull

Already up-to-date.

 

gezhicheng@HIH-D-9030 MINGW64 /d/WorkSpaces/Intellij/scratch (feature_2017_07_06)

$ git stash pop

No stash found.

 

gezhicheng@HIH-D-9030 MINGW64 /d/WorkSpaces/Intellij/scratch (feature_2017_07_06)

$ git push

Counting objects: 223, done.

Delta compression using up to 4 threads.

Compressing objects: 100% (121/121), done.

Writing objects: 100% (223/223), 19.19 KiB | 0 bytes/s, done.

Total 223 (delta 66), reused 0 (delta 0)

remote: Resolving deltas: 100% (66/66), completed with 31 local objects.

remote:

remote: To create a merge request for feature_2017_07_06, visit:

remote:   https://g.hz.netease.com/steam/scratch/merge_requests/new?merge_request%5Bsource_branch%5D=feature_2017_07_06

remote:

To ssh://g.hz.netease.com:22222/steam/scratch.git

   4e926cb..67050b7  feature_2017_07_06 -> feature_2017_07_06

 

也可以直接使用git commitgit pullgit push进行更新。

5.2     使用Intellij

使用git commit提交更改。

使用git pull获取最新代码并合并。


使用git push提交代码。



网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者葛志诚授权发布。