MsgResult
@Data
@SuppressWarnings("unchecked")
public class MsgResult<T> {
/**
* code 错误码
*/
private String code;
/**
* msg 错误信息
*/
private String msg;
/**
* data 返回的数据
*/
private T result;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public MsgResult(String code, String msg, T data) {
this.code = code;
this.msg = msg;
this.result = data;
}
public MsgResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
public MsgResult() {
}
public MsgResult fail(ServerEnum serverEnum){
this.code =serverEnum.getCodePrefix() + ResultCodeEnum.ERROR.getCode();
this.msg = ResultCodeEnum.ERROR.getDesc();
this.result=null;
return this;
}
public MsgResult success(T t){
this.code = ResultCodeEnum.SUCCESS.getCode();
this.msg = ResultCodeEnum.SUCCESS.getDesc();
this.result=t;
return this;
}
public boolean isSuccess(){
return ResultCodeEnum.SUCCESS.getCode().equals(this.code);
}
public static MsgResult successStatic(Object data){
MsgResult result = new MsgResult();
result.code = ResultCodeEnum.SUCCESS.getCode();
result.msg = ResultCodeEnum.SUCCESS.getDesc();
result.result=data;
return result;
}
public static MsgResult failStatic(ServerEnum serverEnum){
MsgResult result = new MsgResult();
result.code =serverEnum.getCodePrefix() + ResultCodeEnum.ERROR.getCode();
result.msg = ResultCodeEnum.ERROR.getDesc();
result.result=null;
return result;
}
}
comomon aop 日志切面
/**日志切面aop
*/
@Component
@Aspect
public class ControllerParamsAspect {
private static Logger logger = LoggerFactory.getLogger(ControllerParamsAspect.class);
private static ThreadLocal<Long> startTime = new ThreadLocal<>();
/* @Pointcut("execution(public com.pld.common.util.MsgResult *..controller..*(..)) " +
"|| execution(public com.pld.common.util.MsgResult *..connector..*(..))")*/
/**
* 如果一个方法内部调用其它方法,如果被调用的方法也满足controllerLog规则,那么在同一个线程里面就会触发多次doBefore和doAfterReturning
* 这时会引起一个问题,只有在最后一个满足的方法里面能正确得到startTime.get()的值,后续的方法都会报空指针错误。因为最后一个方法的doAfterReturning已经执行了startTime.remove(),
* 它们又是在同一个线程里面。
*/
@Pointcut("controllerLog() && !remoteLog()")
public void webLog() {
logger.info("webLog");
}
/**
* 服务内部通过fegin的方式
*/
@Pointcut("execution(public com.pld.common.util.MsgResult *..remote..*(..)) ")
public void remoteLog(){
logger.info("remote log");
};
/**
* controller方法
*/
@Pointcut("execution(public com.pld.common.util.MsgResult *..controller..*(..)) ")
public void controllerLog(){
logger.info("controller log");
};
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
startTime.set(System.currentTimeMillis());
// 记录http请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
logger.warn("获取不到上下文信息");
return;
}
HttpServletRequest request = attributes.getRequest();
// 打印请求内容
logger.info("===============请求内容start===============");
logger.info("请求地址: {}", request.getRequestURL());
logger.info("请求方式: {}", request.getMethod());
logger.info("请求系统标识entCode:{}",request.getHeader("entCode"));
logger.info("请求类方法: {}", joinPoint.getSignature());
logger.info("请求类方法参数: {}", joinPoint.getArgs());
logger.info("===============请求内容end===============");
}
@AfterReturning(pointcut="webLog()", returning="returnObj" )
public void doAfterReturning(JoinPoint joinPoint, Object returnObj) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes == null){
logger.warn("获取不到上下文信息");
return;
}
try{
logger.info("--------------返回内容start----------------");
HttpServletRequest request = attributes.getRequest();
logger.info("请求地址:{}",request.getRequestURL());
logger.info("请求类方法参数: {}", joinPoint.getArgs());
logger.info("返回参数: {}", JSON.toJSONString(returnObj, SerializerFeature.NotWriteDefaultValue));
logger.info("请求处理时间为:" + (System.currentTimeMillis() - startTime.get()));
logger.info("===============返回内容end===============");
startTime.remove();
}catch (Exception e){
logger.warn("doAfterReturning error ",e);
}
}
}
/**
* 2.日记切面 添加MDC 内容
**/
@Component
public class LogInterceptor implements HandlerInterceptor {
@Value("${spring.application.name}")
private String serverName;
/**
* 会话Id
*/
public final static String SESSION_ID = "sessionId";
/**
* 服务名称
*/
public final static String SERVER_NAME="serverName";
private static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String xForwardedForHeader = httpServletRequest.getHeader("X-Forwarded-For");
String sessionId=httpServletRequest.getHeader(LogInterceptor.SESSION_ID);
if (null != sessionId){
MDC.put(SESSION_ID, sessionId);
LOGGER.info("put requestId ({}) to logger", sessionId);
}else{
String localSession = MDC.get(SESSION_ID);
if(null == localSession || localSession.isEmpty()){
sessionId = UUID.randomUUID().toString();
}else{
sessionId = localSession;
}
}
MDC.put(SESSION_ID, sessionId);
String serverNameHeader=httpServletRequest.getHeader(LogInterceptor.SERVER_NAME);
LOGGER.info("HeaderServerName ==" + serverNameHeader);
if(null != serverNameHeader){
MDC.put(SERVER_NAME, serverNameHeader);
}else {
LOGGER.info("serverName ==" + serverName);
MDC.put(SERVER_NAME, serverName);
}
String remoteIp = httpServletRequest.getRemoteAddr();
LOGGER.info("session id:{}, client ip:{}, X-Forwarded-For:{},url :{}", sessionId, remoteIp, xForwardedForHeader,httpServletRequest.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
String uuid = MDC.get(SESSION_ID);
LOGGER.info("remove requestId ({}) from logger", uuid);
MDC.remove(SESSION_ID);
}
}
/**
**2.TypeFilter 排除common包切面,视项目具体情况使用
*/
@Slf4j
public class ExcludeControllerAspectFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
log.info("className:{}", classMetadata.getClassName());
if(classMetadata.getClassName().contains("ControllerParamsAspect")) {
return true;
}
return false;
}
}
统一异常处理controller advice
GlobalExceptionHandle
/**
* 统一异常处理
* @author tlj
* @date 2019/6/3
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandle {
/**
* 全局异常捕捉处理
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public MsgResult errorHandler(Exception ex) {
log.warn("系统错误:{}", ex);
MsgResult msgResult = new MsgResult();
msgResult.setCode(ProductResultCodeEnum.PRODUCT_CENTER_EXCEPTION.getCode());
msgResult.setMsg(ProductResultCodeEnum.PRODUCT_CENTER_EXCEPTION.getMsg());
return msgResult;
}
/**
* 全局异常捕捉处理
*/
@ResponseBody
@ExceptionHandler(value = IllegalArgumentException.class)
public MsgResult errorHandler(IllegalArgumentException ex) {
log.warn("IllegalArgumentException:{}", ex);
MsgResult msgResult = new MsgResult();
msgResult.setCode(ProductResultCodeEnum.PRODUCT_PARAM_ERROR.getCode());
msgResult.setMsg(ProductResultCodeEnum.PRODUCT_PARAM_ERROR.getMsg());
return msgResult;
}
/**
* 不支持该请求方式
*/
@ResponseBody
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public MsgResult errorHandler(HttpRequestMethodNotSupportedException ex) {
log.warn("系统错误:{}", ex);
MsgResult msgResult = new MsgResult();
msgResult.setCode(ProductResultCodeEnum.REQUEST_METHOD_NOT_SUPPORT.getCode());
msgResult.setMsg(ProductResultCodeEnum.REQUEST_METHOD_NOT_SUPPORT.getMsg());
return msgResult;
}
/**
* 参数异常
*/
@ResponseBody
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public MsgResult errorHandler(HttpMessageNotReadableException ex) {
log.warn("参数异常:{}", ex);
MsgResult msgResult = new MsgResult();
msgResult.setCode(ProductResultCodeEnum.PRODUCT_PARAM_ERROR.getCode());
msgResult.setMsg(ProductResultCodeEnum.PRODUCT_PARAM_ERROR.getMsg());
return msgResult;
}
/**
* 三湘银行 异常
*/
@ResponseBody
@ExceptionHandler(value = SXPaymentApiException.class)
public MsgResult sxErrorHandler(SXPaymentApiException pldException) {
log.error("SXPaymentApiException警告:{}", pldException);
MsgResult msgResult = new MsgResult();
msgResult.setCode(pldException.getCode());
msgResult.setMsg(pldException.getMsg());
return msgResult;
}
/**
* 打印到日志为error级别的 异常
*/
@ResponseBody
@ExceptionHandler(value = PldErrorException.class)
public MsgResult sxErrorHandler(PldErrorException pldException) {
ProductResultCodeEnum resultCode = pldException.getResultCode();
log.error("PldErrorException异常:{}", pldException);
MsgResult msgResult = new MsgResult();
msgResult.setCode(resultCode.getCode());
msgResult.setMsg(resultCode.getMsg());
if( pldException.getData()!=null ){
msgResult.setResult(pldException.getData());
}
return msgResult;
}
/**
* 捕获程序显式抛出的异常
*/
@ResponseBody
@ExceptionHandler(value = PldException.class)
public MsgResult pldMessageErrorHandler(PldException pldException) {
ProductResultCodeEnum resultCode = pldException.getResultCode();
log.warn("PldException警告:{}", pldException);
MsgResult msgResult = new MsgResult();
msgResult.setCode(resultCode.getCode());
msgResult.setMsg(resultCode.getMsg());
if( pldException.getData()!=null ){
msgResult.setResult(pldException.getData());
}
return msgResult;
}
/**
* 捕获程序显式抛出的异常(带附加信息)
*/
@ResponseBody
@ExceptionHandler(value = PldDetailException.class)
public MsgResult pldMessageErrorHandler(PldDetailException pldContentException) {
ProductResultCodeEnum resultCode = pldContentException.getResultCode();
log.warn("PldDetailException警告:{}", pldContentException);
MsgResult msgResult = new MsgResult();
msgResult.setCode(resultCode.getCode());
String msgExtra = pldContentException.getExtraMsg();
msgResult.setMsg(resultCode.getMsg()+(StringUtils.isEmpty(msgExtra)?"":(":"+msgExtra)));
return msgResult;
}
/**
* 拦截参数异常
*/
@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public MsgResult validErrorHandler(MethodArgumentNotValidException ex) {
log.warn("参数校验错误:{}", ex);
BindingResult bindingResult = ex.getBindingResult();
StringBuilder sb = new StringBuilder();
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors()) {
sb.append(error.getDefaultMessage()).append(";");
}
}
MsgResult msgResult = new MsgResult();
msgResult.setCode(ProductResultCodeEnum.PRODUCT_NEED_PARAM_EXCEPTION.getCode());
msgResult.setMsg(ProductResultCodeEnum.PRODUCT_NEED_PARAM_EXCEPTION.getMsg() + sb.toString());
return msgResult;
}
}
public enum ProductResultCodeEnum {
SUCC("200", "成功"),
PRODUCT_CENTER_EXCEPTION("26000000", "产品中心服务异常 "),
PRODUCT_NEED_PARAM_EXCEPTION("26000001", "产品中心服务参数错误 "),
PRODUCT_PARAM_ERROR("26000002", "提交的参数有误."),
REQUEST_METHOD_NOT_SUPPORT("26000003", "不支持该请求方式"),
OUTER_SERVICE_ERROR("26000004", "外部服务请求异常"),
ENCRYPT_DECRYPT_ERROR("26000005", "加解密异常"),
SIGN_ERROR("26000006", "签名异常"),
OSS_TOKEN_ERR("26000007", "OSS token获取失败"),
ATTACHMENT_NOT_EXIST("26000008", "附件或图片不存在"),
ATTACHMENT_URL_OBJECT_KEY_NULL("26000009", "附件或图片的url和objectKey不能同时为空"),
DATA_RECORD_NOT_EXIST("26100000", "数据记录不存在"),
SEQUENCE_ERROR("26100001", "序列生成异常"),
SEQUENCE_SIZE_REACH_MAX("26100002", "序列长度已达到设置的最大值"),
CATEGORY_NAME_EXIST("26200001", "分类名称不可重复"),
CATEGORY_CODE_EXIST("26200002", "分类代码不可重复"),
;
private String code;
private String msg;
ProductResultCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
@EqualsAndHashCode(callSuper = true)
@Getter
@Slf4j
public class PldDetailException extends RuntimeException implements Serializable {
private static final long serialVersionUID = -5980209755107175431L;
/**
* code 错误码
*/
final ProductResultCodeEnum resultCode;
final String extraMsg;
public PldDetailException(ProductResultCodeEnum resultCode, String extraMsg) {
super(resultCode.getMsg());
this.resultCode = resultCode;
this.extraMsg=extraMsg;
}
}
junit test
/**
* @author tlj
* @date 2019/5/27
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PldProductCenterApplication.class)
@AutoConfigureMockMvc
@WebAppConfiguration
public abstract class BaseApplicationTest {
protected MockMvc mvc;
@Autowired
private WebApplicationContext context;
@PersistenceContext
protected EntityManager entityManager;
@Before
public void setupMockMvc() {
mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
/**
* 基本断言
* @param action
* @return
* @throws Exception
*/
protected ResultActions basicExpect(ResultActions action) throws Exception {
action.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
action.andExpect(jsonPath("$.code", Matchers.is(ResultCodeEnum.SUCCESS.getCode())))
.andExpect(jsonPath("$.msg", Matchers.is(ResultCodeEnum.SUCCESS.getDesc())));
return action;
}
}
/**
* @author tlj
* @date 2019/10/22
*/
@Slf4j
@Transactional
public class BackendCategoryControllerTest extends BaseApplicationTest{
private static final String TESTID1 ="testId1";
private static final String USERID2 ="userId2";
@Before
public void dataReady(){
// 前置数据
String insetSql ="INSERT INTO `pld_product_center`.`t_base_category` (`id`, `create_by`, `date_created`, `date_last_update`, `status`, `update_by`, `version`, `name`, `bus_status`, `code`, `grade`, `parent_id`) VALUES ('testId1', 'userId1', '2019-10-18 10:55:21', '2019-10-18 11:11:52', '1', 'userId2', '3', 'nameTest123', '0', 'codeTest123', '1', '')";
entityManager.createNativeQuery(insetSql).executeUpdate();
}
/**
* 新增分类
* @throws Exception
*/
@Test
public void categoryAdd() throws Exception {
CategoryAddDTO req = new CategoryAddDTO();
req.setCode("codeTest1");
req.setName("nameTest1");
req.setUserId("userId1");
req.setParentId("");
req.setAuthor("author1");
String requestJson = JSONObject.toJSONString(req);
assert StringUtils.isNotBlank(requestJson);
log.info("新增分类,请求参数{}",requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_ADD )
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 修改分类
* @throws Exception
*/
@Test
public void categoryEdit() throws Exception {
CategoryEditDTO req = new CategoryEditDTO();
req.setId(TESTID1);
req.setName("nameTest123");
req.setUserId(USERID2);
String requestJson = JSONObject.toJSONString(req);
log.info("修改分类,请求参数{}",requestJson);
assert StringUtils.isNotBlank(requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_EDIT )
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 加载同一级分类
* @throws Exception
*/
@Test
public void categoryGradeList() throws Exception {
CategoryGradeListDTO req = new CategoryGradeListDTO();
req.setParentId("");
String requestJson = JSONObject.toJSONString(req);
log.info("加载同一级分类,请求参数{}",requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_GRADE_LIST )
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 分类列表搜索
* @throws Exception
*/
@Test
public void categoryPage() throws Exception {
CategoryPageQueryDTO req = new CategoryPageQueryDTO();
req.setName("name");
req.setBusStatus(1);
req.setCurrentPage(0);
req.setOrderBy("id");
req.setPageSize(10);
req.setXsc("desc");
String requestJson = JSONObject.toJSONString(req);
log.info("分类列表搜索,请求参数{}",requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_PAGE )
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 启用分类
* @throws Exception
*/
@Test
public void categoryOn() throws Exception {
CategoryOnDTO req = new CategoryOnDTO();
req.setId(TESTID1);
req.setUserId(USERID2);
String requestJson = JSONObject.toJSONString(req);
log.info("启用分类,请求参数{}",requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_ON)
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 禁用分类
* @throws Exception
*/
@Test
public void categoryOff() throws Exception {
CategoryOffDTO req = new CategoryOffDTO();
req.setId(TESTID1);
req.setUserId(USERID2);
String requestJson = JSONObject.toJSONString(req);
log.info("禁用分类,请求参数{}",requestJson);
ResultActions action = mvc.perform(MockMvcRequestBuilders.post( Routes.BASE_CATEGORY_OFF)
.contentType(MediaType.APPLICATION_JSON_UTF8).content(requestJson));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
/**
* 查询单个分类
* @throws Exception
*/
@Test
public void categoryGet() throws Exception {
String id = TESTID1;
log.info("查询单个分类,请求参数id:{}",id);
ResultActions action = mvc.perform(MockMvcRequestBuilders.get( Routes.BASE_CATEGORY_GET )
.contentType(MediaType.APPLICATION_JSON_UTF8).param("id",id));
action.andExpect(MockMvcResultMatchers.status().isOk());
basicExpect(action).andReturn();
}
}
/**
* @author tlj
* @date 2019/7/30
*/
public class LianLianReconciliationServiceImplTest extends BaseApplicationTest {
@Autowired
@MockBean
private SftpService fileSystemService;
@Autowired
@MockBean
private ReconciliationService reconciliationService;
/**
* sftp 对账(日明细)
* 1.下载sftp文件(test:使用mockbean,替换为本地对账文件)
* 2.读取对账文件,转换为javabean
* 3.对账(todo:具体对账待实现)
* @throws Exception
*/
@Test
public void reconciliationDateDetail() throws Exception {
List<String> list = fileSystemService.listFiles("/SZ-yuanshi/201906120002511010");
System.out.println("文件列表:");
System.out.println(list);
File file0 = ResourceUtils.getFile("classpath:dateDetailComp.csv");
Mockito.when(fileSystemService.downloadFile(Mockito.any())).thenReturn(file0);
File file = fileSystemService.downloadFile("/SZ-yuanshi/201906120002511010/JYMX_201906120002511010_20190716.csv");
List<ReconciliationDateDetailDTO> dtos = FileUtil.readBuffer(file,ReconciliationDateDetailDTO.class,FileUtil.SPLIT_COMMA);
dtos.stream().forEach(it-> System.out.println(JSONObject.toJSONString(dtos)));
}
/**
* sftp 对账(月明细)
* 1.下载sftp文件(test:使用mockbean,替换为本地对账文件)
* 2.读取对账文件,转换为javabean
* 3.对账(todo:具体对账待实现)
* @throws Exception
*/
@Test
public void reconciliationMonthDetail() throws Exception {
List<String> list = fileSystemService.listFiles("/SZ-yuanshi/201906120002511010");
System.out.println("文件列表:");
System.out.println(list);
File file0 = ResourceUtils.getFile("classpath:monthDetailComp.csv");
Mockito.when(fileSystemService.downloadFile(Mockito.any())).thenReturn(file0);
File file = fileSystemService.downloadFile("/SZ-yuanshi/201906120002511010/JYMX_201906120002511010_20190716.csv");
List<ReconciliationMonthDetailDTO> dtos = FileUtil.readBuffer(file,ReconciliationMonthDetailDTO.class,FileUtil.SPLIT_COMMA);
dtos.stream().forEach(it-> System.out.println(JSONObject.toJSONString(dtos)));
}
/**
* sftp 对账(汇总对账)
* 1.下载sftp文件(test:使用mockbean,替换为本地对账文件)
* 2.读取对账文件,转换为javabean
* 3.对账(todo:具体对账待实现)
* @throws Exception
*/
@Test
public void reconciliationSummary() throws Exception {
List<String> list = fileSystemService.listFiles("/SZ-yuanshi/201906120002511010");
System.out.println("文件列表:");
System.out.println(list);
File file0 = ResourceUtils.getFile("classpath:summaryComp.csv");
Mockito.when(fileSystemService.downloadFile(Mockito.any())).thenReturn(file0);
File file = fileSystemService.downloadFile("/SZ-yuanshi/201906120002511010/JYMX_201906120002511010_20190716.csv");
List<ReconciliationSummaryDTO> dtos = FileUtil.readBuffer(file,ReconciliationSummaryDTO.class,FileUtil.SPLIT_S2);
dtos.stream().forEach(it-> System.out.println(JSONObject.toJSONString(dtos)));
}
}