您觉得本文档还缺少什么内容?可以自己补充~

自动回显

实现代码参考: bc-util-echo-starter 模块.

使用场景

用户表中存储了岗位id(岗位表的主键)、部门id、学历(字典表的code)、民族等字段, 但在 列表页、详情页回显时,需要显示 岗位名、部门名、创建人名、学历名称等。如下表:

用户表(c_user): (字段: id, name, station_id, org_id, nation, education, position_status )

ID 名称 岗位 部门 民族 学历 在职状态
1 测试 研发 研发部 汉族 本科 离职

传统的解决思路:

  1. 表关联查询
  2. 冗余字段
  3. 前端自己解决回显问题
  4. 先单表查询用户的数据,然后在java代码中,循环提取需要回显的org_id、station_id、字典code等, 在批量去查询这些信息后,在循环set到对应的字段中.

解决方案

本项目提供了bc-util-echo-starter模块来解决上面的第四个方案。

  1. pom中加入依赖
  2. yml 开启配置, 更多更详细的配置参考 EchoProperties
bc:
  echo:
    # 是否开启 EchoService 功能
    enabled: true
    # 是否开启EchoResult
    aop-enabled: true
  1. User实体实现EchoVO接口,并在User中加入echoMap .
public class User extends Entity<Long> implements EchoVO {
    @TableField(exist = false)
    private Map<String, Object> echoMap = new HashMap<>();
// ...
}
  1. 在 station_id, org_id, nation, education, position_status 等需要回显的字段上标记注解: @Echo
@Echo(api =  "orgServiceImpl", beanClass = Org.class)
private Long orgId;
@Echo(api =  "stationServiceImpl")
private Long stationId;
@Echo(api =  "dictionaryServiceImpl", dictType = DictionaryType.NATION)
private String nation;
@Echo(api =  "dictionaryServiceImpl", dictType = DictionaryType.EDUCATION)
private String education;
@Echo(api = "dictionaryServiceImpl", dictType = DictionaryType.POSITION_STATUS)
private String positionStatus;
  1. 若User表所在的服务,跟Org、Station、Dictionary表不在同一个服务,请参考5.1,否则参考5.2 。 (由于本项目中,他们是在同一个authority-server服务,所以使用5.2的方式来实现) 5.1. 将上面@Echo注解的api字段,改成OpenFeign的全类名如: com.becypress.product.oauth.api.OrgApicom.becypress.product.oauth.api.StationApicom.becypress.product.oauth.api.DictionaryApi,并将它继承LoadService接口。
@FeignClient(name="bc-authority-server",path="/dictionary")
public interface DictionaryApi extends LoadService {
    @Override
    @GetMapping("/findByIds")
    Map<Serializable, Object> findByIds(@RequestParam(value = "ids") Set<Serializable> ids);
}
@FeignClient(name="bc-authority-server",path="/org")
public interface OrgApi extends LoadService {
    @Override
    @GetMapping("/findByIds")
    Map<Serializable, Object> findByIds(@RequestParam(value = "ids") Set<Serializable> ids);
}
@FeignClient(name="bc-authority-server",path="/station")
public interface StationApi extends LoadService {
    @Override
    @GetMapping("/findByIds")
    Map<Serializable, Object> findByIds(@RequestParam(value = "ids") Set<Serializable> ids);
}

然后在添加Controller,如(EchoController)。代码

public class EchoController {
    @GetMapping("/org/findByIds")
    public Map<Serializable, Object> findUserByIds(@RequestParam(value = "ids") Set<Serializable> ids) {
        return orgService.findByIds(ids);
    }
//....
}

5.2. 将上面@Echo注解中的api字段,实现LoadService接口。 a. Service 继承LoadService

public interface OrgService extends SuperCacheService<Org>, LoadService {}
public interface StationService extends SuperCacheService<Station>, LoadService {}
public interface DictionaryService extends SuperService<Dictionary>, LoadService {}

b. ServiceImpl 实现

public class DictionaryServiceImpl extends SuperServiceImpl<DictionaryMapper, Dictionary> implements DictionaryService {
    @Override
    public Map<Serializable, Object> findByIds(Set<Serializable> types) {
        if (types.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<Serializable, Object> codeValueMap = new HashMap<>();
        types.forEach(type -> {
            Function<CacheHashKey, Map<String, String>> fun = (ck) -> {
                LbqWrapper<Dictionary> wrap = Wraps.<Dictionary>lbQ().eq(Dictionary::getType, type);
                List<Dictionary> list = list(wrap);
                return CollHelper.uniqueIndex(list, Dictionary::getCode, Dictionary::getName);
            };
            Map<String, String> map = cachePlusOps.hGetAll(new DictionaryTypeCacheKeyBuilder().hashKey(type), fun);
            map.forEach((value, txt) -> codeValueMap.put(StrUtil.join(ips.getDictSeparator(), type, value), txt));
        });
        return codeValueMap;
    }

}

// OrgServiceImpl 和 StationServiceImpl 的实现参考源码
  1. 然后在UserController的page方法中,调用echoService.action
public class UserController {
    @Override
    public void query(PageParams<UserPageQuery> params, IPage<User> page, Long defSize) {
          //  先查数据库, 将page查出数据
        // 手动调用此方法实现数据回显
        echoService.action(page);
    }
}
  1. 调用该接口后,会在User的echoMap中put需要显示的值。

原理

  1. 项目启动时,EchoService构造方法通过策略模式注入Map strategyMap, 这个strategyMap的key就是 @Echo注解中的api 字段, strategyMap的value就是所有实现了LoadService接口的实现类 或 OpenFeign。

注意: 实现LoadService的类,必须被Spring管理!

  1. 项目运行时, 调用了echoService.action(obj) 方法后,实现echoMap的填充。

  2. echoService.action方法逻辑如下:

/**
 * 回显数据的3个步骤:(出现回显失败时,认真debug该方法)
 * <p>
 * 1. parse: 通过反射将obj的字段上标记了 @Echo 注解的字段解析出来, 封装到typeMap中
 * 2. load: 依次查询待回显的数据
 * 3. write: 将查询出来的结果 反射或put 到obj的 字段或echoMap 中
 * <p>
 * 注意:若对象中需要回显的字段之间出现循环引用,很可能发生异常,所以请保证不要出现循环引用!!!
 *
 * @param obj          需要回显的参数,支持 自定义对象(User)、集合(List<User>、Set<User>)、IPage
 * @param ignoreFields 忽略字段
 */
public void action(Object obj, String... ignoreFields)  {//...}
  1. 最后要自己实现LoadService的findByIds方法

常见问题

  1. 跨服务回显时,@Echo中的api需要指定为 FeignClient 的全类名。

如:消息服务,需要回显权限服务的 用户名:@Echo(api="com.becypress.product.oauth.api.UserApi")

  1. 单个服务回显时,@Echo中的api需要指定为 ServiceImpl 的Spring id。

如:权限服务,需要回显权限服务的 用户名:@Echo(api="userServiceImpl")

results matching ""

    No results matching ""