本地化部署后定制解决方案
本人做过很多互联网项目,也做过很多企事业单位的项目,比如监管系统,临床试验系统等,这些系统主要是看守所,医院在用,这些单位对网络安全比较重视,都需要本地化部署,因为每个单位对系统的需求不同,所以还需要定制开发,如何管理base版本和定制化版本,是个大问题,现提供几种思路。
1.通过分支区分版本
如果不需要定制化开发,直接部署base版本即可,如果需要定制化开发,则从base版本中拉一个分支,在这个分支中开发,但是后续升级会很麻烦,所以这种方案是不考虑升级的,优点是实现简单,缺点是无法升级,只有特定情况下才会使用这种方案。
2.通过新建目录的方式打补丁
比如我要定制化一个ny的版本,就新建一个ny的包,里面也包含controller,service,mapper等,跟主包的目录层级一样,


在配置环境中加一个配置,用来确定当前环境,比如基准版本用base,ny版本就用ny
system.config.context=ny基准版本要做差异化的部分加上条件渲染,比如ny版本用户管理的功能有差异


这样写后这两个类只会在base环境下才会生成bean
然后我们生成ny下的controller和service
controller代码:
要继承UserController,这样原有的功能都会继承,我们只需要写差异化代码就行了,比如原来有listPage接口,但是不符合ny版本要求,需要改造,直接覆盖这个方法就行了,又比如要加新功能,则直接加新接口就行了,这样还有个好处是基准版本加了新功能,ny版本可以直接继承,这样升级起来就很方便了
@RestController
@RequestMapping("/admin/user")
@Api(tags = "ny用户管理")
@ConditionalOnProperty(prefix = "system.config.", name="context", havingValue = "ny")
public class NyUserController extends UserController {@Autowiredprivate NyUserService nyUserService;@ApiOperation(value = "ny分页查询用户,支持动态拼参,排序")@ApiImplicitParams({@ApiImplicitParam(name = "page", value = "当前页码", dataType = "int", paramType = "query", required = false),@ApiImplicitParam(name = "rows", value = "每页记录数", dataType = "int", paramType = "query", required = false),@ApiImplicitParam(name = "orderItem", value = "排序字段", dataType = "String", paramType = "query", required = false),@ApiImplicitParam(name = "sortDir", value = "排序方式", dataType = "String", paramType = "query", required = false)})@GetMapping("listPage")public Result<UserDto> listPage(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int rows, String orderItem, String sortDir, HttpServletRequest request) {System.out.println("我修改了base接口");Map<String, Object> searchParam = Servlets.getParametersStartingWith(request, PARAM_PREFIX);IPage<UserDto> iPage = nyUserService.listPage(searchParam, orderItem, sortDir, page, rows);return resultOk(iPage);}@ApiOperation(value = "查询用户总数")@GetMapping("getUserCount")public Result getUserCount() {int count = nyUserService.getUserCount();return resultOk(count);}
}service代码:
service层也是同理,继承UserService
public interface NyUserService extends UserService {int getUserCount();
}在实现类中,我们新建了一个NyUserMapper,这样有差异化的代码都可以写在这个mapper里面
@Service
@Transactional(rollbackFor = Exception.class)
@Slf4j
@ConditionalOnProperty(prefix = "system.config.", name="context", havingValue = "ny")
public class NyUserServiceImpl extends UserServiceImpl implements NyUserService {@Autowiredprivate NyUserMapper nyUserMapper;@Overridepublic IPage<UserDto> listPage(Map<String, Object> searchParam, String orderItem, String sortDir, int page, int rows) {System.out.println("我修改了service");return super.listPage(searchParam, orderItem, sortDir, page, rows);}@Overridepublic int getUserCount() {return nyUserMapper.getUserCount();}
}mapper代码:
public interface NyUserMapper extends BaseMapper<User> {int getUserCount();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itrial.itrialpay.ny.mapper.NyUserMapper"><select id="getUserCount" resultType="java.lang.Integer">select count(*) from user</select>
</mapper>扫描包方面的改动,我用的是mybatis-plus,要扫描到子目录的mapper文件中间需要加**
#mybatis-plus配置
mybatis-plus.mapper-locations=classpath*:mapper/**/*.xml同理,启动类上面包扫描也要加**

swagger改造,我试了下swagger好像不支持**,所以用了basePackage的or,支持多个包,当然也可以直接指定com.itrial.itrialpay这样下面所有包都能扫到
@Beanpublic Docket adminApi() {List<ResponseMessage> responseMessageList = new ArrayList();responseMessageList.add((new ResponseMessageBuilder()).code(200).message("请求成功").build());return new Docket(DocumentationType.SWAGGER_2).directModelSubstitute(LocalDateTime.class, Date.class).directModelSubstitute(LocalDate.class, Date.class).directModelSubstitute(LocalTime.class, Date.class).groupName("管理端接口").globalResponseMessage(RequestMethod.GET, responseMessageList).globalResponseMessage(RequestMethod.POST, responseMessageList).globalResponseMessage(RequestMethod.PUT, responseMessageList).globalResponseMessage(RequestMethod.DELETE, responseMessageList).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.itrial.itrialpay.controller.admin").or(RequestHandlerSelectors.basePackage("com.itrial.itrialpay." + systemConfig.getContext() + ".controller.admin"))).build().securitySchemes(securitySchemes()).securityContexts(securityContexts());}