[Spring cloud 一步步实现广告系统] 6. Service实现&Zuul配置&Test
dao层设计实现
这里我们使用spring data jpa
来实现数据库操作,当然大家也可以使用mybatis
,都是一样的,我们依然以用户表操作为例:
/** * aduserrepository for 用户数据库操作接口 * 继承自jparepository<aduser, long>,第一个参数aduser代表当前要操作的实体类的class定义,第二个参数long表示该类的主键类型 * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang</a> */ public interface aduserrepository extends jparepository<aduser, long> { /** * 根据用户名称获取用户 * * @param username 名称 * @return 用户对象 */ aduser findbyusername(string username); list<aduser> findallbyusername(string username); }
-
jparepository 的默认实现方法,如果我们只是继承了
jparepository
而没有实现具体的操作方法,我们也是可以通过使用它的默认方法来做crud
操作的,如下:
功能service实现
创建service package,依然以用户操作为例,创建com.sxzhongf.ad.service.iuserservice
和com.sxzhongf.ad.service.impl.userserviceimpl
,userserviceimpl
实现了iuserservice
。
- 创建
iuserservice
接口
/** * iuserservice for 用户service * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ public interface iuserservice { /** * 创建用户接口 * * @param userrequestvo {@link userrequestvo} * @return {@link userresponsevo} * @throws adexception 错误 */ userresponsevo createuser(userrequestvo userrequestvo) throws adexception; list<aduser> findallbyusername(string username); }
- 使用
iuserservice
接口
/** * userserviceimpl for 用户service * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ @slf4j @service public class userserviceimpl implements iuserservice { private final aduserrepository userrepository; @autowired public userserviceimpl(aduserrepository userrepository) { this.userrepository = userrepository; } /** * 创建用户 * * @param userrequestvo {@link userrequestvo} * @return result {@link userresponsevo} */ @override @transactional public userresponsevo createuser(userrequestvo userrequestvo) throws adexception { if (!userrequestvo.validate()) { log.error("request params error: {}", userrequestvo); throw new adexception(constants.errormessage.request_param_error); } //查重 aduser existuser = userrepository.findbyusername(userrequestvo.getusername()); if (existuser != null) { log.error("{} user is not exist.", userrequestvo.getusername()); throw new adexception(constants.errormessage.user_exist); } aduser user = userrepository.save(new aduser(userrequestvo.getusername(), commonutils.md5(userrequestvo.getusername()))); log.info("current user is : {}", user); return new userresponsevo(user.getuserid(), user.getusername(), user.gettoken(), user.getcreatetime(), user.getupdatetime()); } @override public list<aduser> findallbyusername(string username) { return userrepository.findallbyusername(username); } }
-
创建数据传输对象(dto/vo)
其实好多人在这里都会特别郁闷,搞不清楚这些命名有什么区别,个人建议是大家不用纠结,dto(data transfer object),就是表示我们在各个层传递的对象,vo在展示层操作的对象。但是这个只是个命名,它的本质就是一个object, 你传递到dao层可以吗?当然可以,你传单独字段都是可以的。所以,没必要过分纠结这种信息,咬文嚼字有时候反而会适得其反。
/** * userrequestvo for 创建用户请求对象vo * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ @data @allargsconstructor @noargsconstructor public class userrequestvo { private string username; public boolean validate() { return !stringutils.isempty(username); } } --- /** * userresponsevo for 用户响应vo * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ @data @allargsconstructor @noargsconstructor public class userresponsevo { private long userid; private string username; private string token; private date createtime; private date updatetime; }
- 因为报错信息有可能是相同的,那我们抽取一个常量类来封装。
/** * constants for todo * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ public class constants { /** * 通用错误信息异常类 */ public static class errormessage { public static final string request_param_error = "请求参数异常"; public static final string user_exist = "用户已存在"; public static final string user_not_exist = "用户不存在"; } }
- 在common project 下面创建一个工具类
com.sxzhongf.ad.common.utils.commonutils
,用来对用户username进行md5加密来获取token信息。
/** * commonutils for 工具类 * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ @slf4j public class commonutils { /** * md5 加密 */ public static string md5(string value) { return digestutils.md5hex(value).touppercase(); } }
参考创建用户的实现,依次实现其他表操作。
controller实现
依然以用户功能实现为例:
/** * usercontroller for 用户controller * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang | 若初</a> */ @restcontroller @slf4j @requestmapping("/user") public class usercontroller { @autowired private iuserservice userservice; @postmapping(path = "/create") public userresponsevo createuser(@requestbody userrequestvo requestvo) throws adexception { log.info("ad-sponsor: createuser -> {}", json.tojsonstring(requestvo)); return userservice.createuser(requestvo); } @getmapping(path = "/get") public commonresponse getuserlist(@param(value = "username") string username) throws adexception { log.info("ad-sponsor: getuserlist -> {}", json.tojsonstring(username)); return new commonresponse(userservice.findallbyusername(username)); } }
在网关中配置广告投放系统
我们在投放系统的配置中,配置了server.servlet.context-path:/ad-sponsor
这么一个路径,意味着所有请求当前系统的路径都需要带有ad-sponsor, 例如:http://xxx/ad-sponsor/user/get?username=yyy
,这是网关请求所必需的。根据上述,我们在网关服务中配置我们当前的投放系统:
spring: application: name: ad-gateway-zuul server: port: 1111 eureka: client: service-url: defaultzone: http://server1:7777/eureka/,http://server2:8888/eureka/,http://server3:9999/eureka/ instance: hostname: ad-gateway-zuul ############################################## # 以下为重要信息 zuul: ignored-services: '*' # 过滤所有请求,除了下面routes中声明过的服务 # 配置网关路由规则 routes: sponsor: #在路由中自定义服务路由名称 path: /ad-sponsor/** serviceid: mscx-ad-sponsor #微服务name strip-prefix: false search: #在路由中自定义服务路由名称 path: /ad-search/** serviceid: mscx-ad-search #微服务name strip-prefix: false prefix: /gateway/api strip-prefix: false #不对 prefix: /gateway/api 设置的路径进行截取,默认转发会截取掉配置的前缀
test
-
直接访问投放系统
调用
curl -g http://localhost:7000/ad-sponsor/user/get?username=isaac%20zhang
,返回结果:
{ code: 0, // 统一成功标示 message: "success", // 统一处理结果message data: [ // 具体的对象信息 { userid: 10, username: "isaac zhang", token: "2d3abb6f2434109a105170fb21d00453", userstatus: 1, createtime: 1561118873000, updatetime: 1561118873000 } ] }
-
通过网关调用
因为我在网关配置中加了前缀
prefix: /gateway/api
,因此,我们访问的时候需要添加上这个前缀信息,否则会报404错误。curl -g http://localhost:1111/gateway/api/ad-sponsor/user/get?username=isaac%20zhang
,我们发现结果并没有按照我们想象的展示出来。bogon:~ zhangpan$ http://localhost:1111/gateway/api/ad-sponsor/user/get?username=isaac%20zhang -bash: http://localhost:1111/gateway/api/ad-sponsor/user/get?username=isaac%20zhang: no such file or directory
为什么呢?我们来查看一下日志:
2019-07-27 20:44:19.093 info 4766 --- [nio-1111-exec-4] c.s.a.g.filter.validatetokenfilter : get request to http://localhost:1111/gateway/api/ad-sponsor/user/get 2019-07-27 20:44:19.093 warn 4766 --- [nio-1111-exec-4] c.s.a.g.filter.validatetokenfilter : access token is empty 2019-07-27 20:44:19.098 info 4766 --- [nio-1111-exec-4] c.s.ad.gateway.filter.accesslogfilter : request "/gateway/api/ad-sponsor/user/get" spent : 0 seconds. 2019-07-27 20:48:37.801 info 4766 --- [trap-executor-0] c.n.d.s.r.aws.configclusterresolver : resolving eureka endpoints via configuration
我们可以清晰的看到,
validatetokenfilter : access token is empty
,为什么会有这么一个报错呢?那是因为我在配置网关的时候,添加了一次拦截:/** * validatetokenfilter for 服务token校验 * * @author <a href="mailto:magicianisaac@gmail.com">isaac.zhang</a> */ @slf4j @component public class validatetokenfilter extends zuulfilter { ... @override public object run() throws zuulexception { requestcontext ctx = requestcontext.getcurrentcontext(); httpservletrequest request = ctx.getrequest(); log.info(string.format("%s request to %s", request.getmethod(), request.getrequesturl().tostring())); object accesstoken = request.getheader("accesstoken"); //.getparameter("accesstoken"); if (accesstoken == null) { log.warn("access token is empty"); ctx.setsendzuulresponse(false); ctx.setresponsestatuscode(401); // ctx.setresponsebody(body)对返回body内容进行编辑 return null; } log.info("access token ok"); return null; } }
观察代码我们发现,会从
requestheader
中获取accesstoken
参数,我们没有提供,当然就会报错了呀。接下来,我们提供上该参数再试:bogon:~ zhangpan$ curl -h "accesstoken:true" http://localhost:1111/gateway/api/ad-sponsor/user/get?username=isaac%20zhang ---返回 {"code":0,"message":"success","data":[{"userid":10,"username":"isaac zhang","token":"2d3abb6f2434109a105170fb21d00453","userstatus":1,"createtime":1561118873000,"updatetime":1561118873000}]}
至此,我们的广告投放系统简单功能已经全部实现完毕,并且可以通过网关进行转发。
上一篇: 通俗地说决策树算法(二)实例解析
下一篇: 山药炖鸭的做法告诉你
推荐阅读
-
[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用
-
[Spring cloud 一步步实现广告系统] 6. Service实现&Zuul配置&Test
-
[Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类
-
[Spring cloud 一步步实现广告系统] 4. 通用代码模块设计
-
[Spring cloud 一步步实现广告系统] 16. 增量索引实现以及投送数据到MQ(kafka)
-
[Spring cloud 一步步实现广告系统] 12. 广告索引介绍
-
[Spring cloud 一步步实现广告系统] 7. 中期总结回顾
-
[Spring cloud 一步步实现广告系统] 6. Service实现&Zuul配置&Test
-
[Spring cloud 一步步实现广告系统] 11. 使用Feign实现微服务调用
-
[Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类