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)));
}
}