用户填写个人信息时,需要通过下拉列表选择省份和城市。这个功能可以前端通过js从某个源获取。这种方法存在一个问题,当省份或城市的名称有变动时,会造成不一致的情况。针对这一问题,解决方案是——通过底层提供的web服务实现此功能。其中,用户选择的省份和城市均用code(区域编号)存储,根据code显示其名称。如此以来,当城市名称变动,只需要再数据库底层修改对应数据即可,对scratch服务没有影响。
所以,“大概”需要两个接口……
1. 获取所有省份列表,省份信息包括[省份code]、[省份名称]
2. 获取某省份下的所有城市列表,城市信息包括[城市code]、[城市名称]
为什么说“大概”?其实这种口头的接口描述是不靠谱的。编程的正确顺序是——自顶向下设计,自底向上实现。所以要实现某一功能接口,首先要和前端工程师进行接口约定。
前端新建两个接口。
设置请求和相应信息。
好了,可以放心地编程了。
Meta:
定义: 和DB一一对应的JAVA类型
使用场景: 主要在 dubbo-provider 内部使用
DTO:
定义: 和基础服务逻辑对应的JAVA对象,主要表现为持有Meta里部分或全部字段,并在此基础上增加业务需要的字段(可以是基本类型,也可以是其他DTO类型)
使用场景: 主要在 dubbo-api 里定义并使用
备注: 由于分层概念,Meta是不暴露给外层调用者的,所以禁止DTO直接继承Meta。
BO:
定义: 和组合服务逻辑对应的JAVA对象,主要表现为持有DTO里部分或全部字段,并在此基础上增加业务需要的字段(可以是基本类型,也可以是其他VO类型)
使用场景: 主要在 manager层 里定义并使用
备注: 由于分层概念,禁止BO直接继承DTO。
VO:
定义: 和Web层对应的JAVA对象
在上图所示的分层模型中,Service层及依赖的DB层之间相互隔离,以便进行拆分。Service层提供基础服务,其API暴露给外界,提供RPC远程调用。Manger为组合服务,根据业务逻辑可能调用多个Service。同样,Controller也可能调用多个Manager。这种分层架构方便解耦,易于业务拆分和扩展。
@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语句,可以据此建立数据表。
public interface Dao {
List<City> listByCode(Long code);
}
没啥可说的。
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文件。
以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。
public interface SomeService {
/**
* @param code.
* @return
* @throws MiscServiceException
*/
List<DTO> queryMetaByCode(Long code) throws MiscServiceException;
}
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);
}
}
以BO为例。
@Data
public class BO implements Serializable {
private static final long serialVersionUID = -5138268045097578727L;
private Long code;
}
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);
}
}
以VO为例。
@Data
public class VO implements Serializable {
private static final long serialVersionUID = 6575346609598828802L;
private Long code;
}
下文详述。
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());
}
}
}
至此,自底向上基本流程完成。
单元测试通常测试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());
}
}
此处要注意的是,使用断言而不是打印,目的是方便项目构建时自动进行测试。
然后点击绿色箭头进行执行。
可以通过进度条看到运行结果。
新建如下配置进行maven构建。
新建如下配置运行工程。
执行后,通过http请求访问该接口。
上图是执行成功的情况。如果执行不成功,请检查:
1. 是否开启了VPN。如未启动,参考http://ks.netease.com/blog?id=7829。
1、是否同步了最新代码。如未同步,参考下一节。
首先使用如下命令,将本地分支和远程分支建立关联。
$ git branch --set-upstream feature_2017_07_06 origin/feature_2017_07_06
之后进行pull和push时就无需带参数。
分别使用如下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 commit、git pull、git push进行更新。
使用git commit提交更改。
使用git pull获取最新代码并合并。
使用git push提交代码。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者葛志诚授权发布。