springcloud微服务架构实战:商家管理微服务设计
商家管理微服务设计
商家管理微服务是一个独立的RESTAPI应用,这个应用通过接口服务对外提供商家信息管理、商家权限管理和菜单资源管理等方面的功能。
商家管理微服务开发在merchant-restapi模块中实现,有关这一类型模块的依赖引用、配置、启动程序的设计等,可以参考前面章节中有关RESTAPI微服务开发中的相关说明,不再重复。
商家管理微服务将直接调用权限管理模型的领域服务,在调用之前,我们可以对领域服务层进行一个单元测试,以验证领域服务层的程序正确性。同时,也可以通过单元测试生成一个管理员用户,以方便后面的操作体验。
商家管理服务层单元测试
首先,在merchant-restapi模块中,对10.1节开发的各个领域服务进行测试,从而对整个商家业务领域的开发进行全面的验证。这些测试包括各个实体的创建、数据获取、对象更新、删除和分页查询等内容。
创建商家及其用户实体的测试用例如下所示:
@RunWith (Spr ingRunner.class)
@ContextConfiguration(classes = {JpaConfiguration.class,
MerchantRestApiAppl ication.class})
@SpringBootTes t
public class UserTest {
private static Logger logger = LoggerFactory. getLogger (UserTest.class) ;
@Autowi red
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private ResourceService resourceService;
CAutowired
private ModelService modelService;
CAutowired
private KindService kindService;
@Autowired
private MerchantService merchantService;
@Test
public void insertData() {
Kind kind = new Kind() ;
kind.setName("商家系统");
kind. setLink ("merchantweb") ;
kindService. save (kind) ;
Assert .notNull (kind.getId(), "create kind error") ;
Model model = new Model () ;
model . setName("用户管理") ;
model. setHost ("/user/ index") ;
model. setKind(kind) ;
modelService.save (model) ;
Assert. notNull (model.getId(),"create model error") ;
Resource resource = new Resource() ;
resource.setName("用户修改");
resource.setUrl ("/user/edit/**") ;
resource . setModel (model) ;
resourceService. save (resource) ;
Assert. notNull (resource.getId(), "create resource error") ;
Role role = new Role() ;
role.setName("商家管理员");
List resources = new ArrayList<>() ;
resources. add (resource) ;
role. setResources (resources) ;
roleService.save (role) ;
Assert. notNull (role.getId(),"create role error") ;
Merchant merchant = new Merchant() ;
merchant . setName ("测试商家") ;
merchantService. save (merchant) ;
Assert .notNull (merchant.getId(), "create merchant error") ;
User user = new User() ;
user. setName ("admin") ;
BCryptPasswordEncoder bpe = new BCryptPasswordEncoder() ;
user. setPassword (bpe. encode ("123456")) ;
user . setEmail ("admin@com.cn") ;
List roles = new ArrayList<>() ;
roles.add(role) ;
user . setRoles (roles) ;
user . setMerchant (merchant) ;
userService. save (user) ;
Assert .notNull (user.getId(), "create user error");
}
}
在这个测试用例中,包含了商家业务模型中所有实体的创建,这些实体包括分类、模块、资源、角色、商家、用户等。如果测试通过,则可以生成一个由分类、模块和资源组成的三级菜单,同时创建一个具有所属商家、 具有一个角色和相关访问资源权限的用户实体。这个用户实体的用户名和密码为“admin/123456”。在后面的开发中,我们可以使用这个用户来登录系统。
如果测试不能通过,则可以根据断言中提示的错误信息,在相关的服务组件中查找出错的原因。
获取实体的测试用例如下所示:
@Test
public void getData() {
User user = userService. findOne (1L) ;
Assert.notNull (user, "not find") ;
logger . info("====user==={}", new Gson() . toJson (user));
}
这个测试用例通过用户ID获取用户信息,如果测试通过,则输出用户实体的完整信息,包括用户、用户拥有的角色和角色包含的资源等。
分页查询的测试如下所示:
@Test
public void findAll() throws Exception{
SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse ("2017-01-0100:00:00") ;
UserQo userQo = new UserQo() ;
userQo. setCreated(date) ;
Merchant merchant
merchantService. findOne (1L) ;
MerchantQo merchantQo = CopyUtil.copy (merchant, MerchantQo .class);
userQo. setMerchant (merchantQo) ;
Page page = userService. findA1l (userQo) ;
Assert.notEmpty (page .getContent(), "list is empty");
List list = page. getContent() ;
for(User user : list) {
logger. info("====user===={},", new Gson() .toJson(user));
}
}
这个测试用例使用查询对象UserQo配置了分页查询的参数,来执行用户信息的分页查询。
在查询参数中设定了创建日期和所属商家等属性。在查询成功后,将输出每条记录的信息,这些信息有用户对象、用户拥有的角色、角色关联的资源和资源所属的模块等。
其他有关更新和删除等测试,可以参照上面的方法进行设计。
单元测试在进行工程打包时,可以作为程序正确性的一一个验证手段。如果测试不通过,则不能成功打包。当使用Maven进行项目管理时,这项功能默认是打开的。如果想要在关闭打包时执行测试,可以在工程中使用下面所示的配置:
<plugin>
<group Id>org. apache . maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.20version>
<configuration>
<skipTests>trueskipTests>
configuration>
plugin>
商家服务的接口开发
在商家管理的REST API应用中,包含了商家信息管理、商家用户权限管理和菜单资源管理等接口的开发。每一个接口的设计我们分别使用一个RestController来实现。这些接口的设计基本上大同小异,下面我们以用户接口的设计为例进行说明。
用户的查询接口是使用GET方法实现的,几种查询接口的实现方法如下所示:
@RestController
@RequestMapping("/user")
public class UserController
private static Logger logger 二LoggerFactory .getLogger (UserController.
class) ;
@Autowi red
private UserService userService;
@RequestMapping("/{id}")
public String findById (EPathVariable Long id) {
return new Gson() . toJson (userService. findOne(id));
@RequestMapping ("/names/ {name}")
public String findByName (@PathVariable String name) {
return new Gson() . toJson (userService. findByName (name)) ;
@RequestMapping("/list")
public String findList() {
return new Gson() . toJson (userService. findAll());
@RequestMapping (value = "/page")
public String findPage (Integer index, Integer size, String name, Long
merchantId) {
try {
UserQo userQo = new UserQo() ;
if (Commonutils. isNotNull (index)) {
userQo. setPage (index) ;
if (CommonUtils. isNotNull (size)) {
userQo.setSize (size) ;
}
if (CommonUtils. isNotNull (name)) {
userQo. setName (name) ;
if (CommonUtils. isNotNull (merchantId)) {
MerchantQo merchantQo = new MerchantQo();
merchantQo . setId (merchantId) ;
userQo. setMerchant (merchantQo) ;
Page users = userService. findAll (userQo) ;
Map<String, object> page = new HashMap<>() ;
page.put ("content", users .getContent());
page .put ("totalPages", users . getTotalPages();
page.put ("totalelements", users . getTotalElements());
return new Gson() . toJson(page) ;
} catch (Exception e) {
e. printStackTrace() ;
return null ;
}
}
这些查询接口有单个对象查询、列表查询和分页查询等。因为是接口调用,所以查询的结果最终都是以JSON结构的方式返回文本数据。
如果要新建-一个商家用户,则可以使用POST方法实现,代码如下所示:
@RestController
ERequestMapping ("/user")
public class UserController
private static Logger logger =
LoggerFactory . getLogger (UserController.class) ;
@Autowired
private UserService userService;
CRequestMapping (value=" /save", method = RequestMethod. POST)
public String save (@RequestBody UserQo userQo) throws Exception{
User user = CopyUtil. copy (userQo, User.class);
List roleList = CopyUtil. copyList (userQo . getRoles(), Role.class);
user.setRoles (roleList) ;
user.setMerchant (CopyUtil. copy (userQo. getMerchant (),Merchant.class));
String ret = userService. insert (user) ;
logger. info("新增=" + ret) ;
return ret;
}
}
当创建实体提交给数据服务进行处理时,必须将输入参数中的查询对象转化为实体,使用实体调用领域服务进行数据保存。并且在创建-一个商家用户实体时,为了保证商家用户的合法性,还必须指定用户的所属商家,并且给其分配一个角色,这样,这个商家用户才可以用来登录商家系统。
商家用户的更新设计可以使用PUT方法实现,代码如下所示:
@RestController
@RequestMapping ("/user")
public class UserController
private static Logger logger =
LoggerFactory.getLogger (UserController.class) ;
@Autowi red
private UserService userService;
@RequestMapping (value=" /update", method
RequestMethod. PUT)
public String update (@RequestBody UserQo userQo) throws Exception{
User user = CopyUtil.copy (userQo, User. class);
List roleList = CopyUtil.copyList (userQo.getRoles(), Role.class);
user .setRoles (roleList) ;
user . setMerchant (CopyUtil.copy (userQo. getMerchant (), Merchant.class));
String ret = userService. update (user);
logger. info("修改="+ ret) ;
return ret;
}
}
商家用户的更新设计与创建一个商 家用户的实现方法相差不多,不同之处在于请求方法及传输的参数。
删除一个商家用户的设计可以使用DELETE方法实现,代码如下所示:
@RestController
@RequestMapping (" /user")
public class UserController
private static Logger logger = LoggerFactory. getLogger (UserController .
class) ;
@Autowired
private UserService userService;
@Reques tMapping (value="/delete/{id}",method = RequestMethod . DELETE)
public String delete(@Pathvariable Long id) throws Exception {
String ret = userService .delete(id) ;
logger. info("删除=" + ret) ;
return ret;
}
}
当要删除的实体具有关联关系时,则必须先删除它们之间的关联关系,然后才能执行删除操作。例如,在角色删除的设计中,使用了如下所示的设计: .
@RequestMapping (value="/delete/ {id}", method = RequestMethod. DELETE)
public String delete (@PathVariable Long id) throws Exception {
//让具有此角色的用户脱离关系
List userList = userService. findByRoleId(id) ;
if (userList != null && userList.size() > 0) {
for(User user : userList) {
for (Role role : user.getRoles()) {
if(role.getId() .equals(id)) {
user .getRoles() . remove (role) ;
userService. update (user) ;
break;
}
}
}
}
//安全删除角色
String ret = roleService.delete(id) ;
logger. info("删除=" + ret) ;
return ret;
}
即在删除角色之前,要保证角色没有被用户关联。如果已经存在关联关系,则必须将这些关联关系删除之后,才能成功删除角色。
在完成接口开发之后,可以启动REST API应用,对一些查询接口可以使用浏览器进行-一个简单的测试。例如,对于用户信息的分页查询,可以使用如下所示的链接进行测试:
http://localhost: 9081/user/page
如果数据库中存在商家用户数据,则打开链接之后,可以看到如图10-3所示的JSON结构的数据。
对于上面设计的这些接口调用方法,我们都以FeignClient的方式进行了封装。更详细的信息可以参照前面章节中相关内容的说明。商家服务的接口调用设计,在模块merchant-client 中实现。在后面的开发中,我们只需在项目管理中配置模块merchant-client的依赖引用,就可以使用这些接口调用方法实现商家管理的各项功能设计了。
本文给大家讲解的内容商家管理后台与sso设计:商家管理微服务设计
下篇文章给大家讲解的是商家管理后台与sso设计:SSO设计;
觉得文章不错的朋友可以转发此文关注小编;
感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。