# React聚合项目启动
前端是多个 React 项目聚合页面,统一入口,认证是使用的 Cookies 保存 Token 顶级域名的共享机制来获取 Token 认证的,然后需要做一下本地域名的绑定,需要修改 host 文件及启动 Nginx 反向代理
# 本地Host配置
# 顶级域名使用的 xxx-k8s.xxx.com
127.0.0.1 page-web.xxx-k8s.xxx.com
127.0.0.1 product-web.xxx-k8s.xxx.com
127.0.0.1 channel-web.xxx-k8s.xxx.com
访问记得加上前端项目端口,因为 host 只能映射到 IP
- https://page-web.xxx-k8s.xxx.com:8001
- https://channel-web.xxx-k8s.xxx.com:8000
# Nginx本地域名做反向代理
server_names_hash_bucket_size 64;
server {
listen 80;
# 聚合页面,统一入口
server_name page-web.xxx-k8s.xxx.com;
root /root/app/dist;
client_max_body_size 20m;
location / {
proxy_buffering off;
proxy_set_header Host page-web.xxx-k8s.xxx.com;
# client_header_timeout 300s; 默认为60秒
client_body_timeout 300s;
proxy_connect_timeout 300s; # 连接超时 默认为60秒
proxy_read_timeout 300s; # 读取超时 默认为60秒
proxy_send_timeout 300s; # 发送超时 默认为60秒
# proxy_pass http://localhost:5000;
proxy_pass http://localhost:8001;
}
}
server {
listen 80;
# 产品中心
server_name product-web.xxx-k8s.xxx.com;
root /root/app/dist;
client_max_body_size 20m;
location / {
proxy_buffering off;
proxy_set_header Host product-web.xxx-k8s.xxx.com;
# client_header_timeout 300s; 默认为60秒
client_body_timeout 300s;
proxy_connect_timeout 300s; #连接超时 默认为60秒
proxy_read_timeout 300s; #读取超时 默认为60秒
proxy_send_timeout 300s; #发送超时 默认为60秒
proxy_pass http://localhost:8081;
}
}
server {
listen 80;
server_name channel-web.xxx-k8s.xxx.com;
root /root/app/dist;
client_max_body_size 20m;
location / {
proxy_buffering off;
# proxy_set_header Host
# client_header_timeout 300s; 默认为60秒
client_body_timeout 300s;
proxy_connect_timeout 300s; #连接超时 默认为60秒
proxy_read_timeout 300s; #读取超时 默认为60秒
proxy_send_timeout 300s; #发送超时 默认为60秒
proxy_pass http://localhost:8000;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
server {
listen 9898;
location / {
root html;
index index.html index.htm;
}
}
聚合页面项目 page-web
本地开发可以建立 env/local.js
文件
module.exports = {
'channel-hub': {
target: 'http://channel-web.xxx-k8s.xxx.com:8000/',
spa: true,
},
'product-center': {
target: 'http://product-web.xxx-k8s.xxx.com/',
spa: true,
}
}
后端启动,改下 nacos 的配置地址,端口
# 分库分表查询
数据库都分了 2 个库,一个库 16 个表,总共 32 个表,开发测试如何快速判断数据在哪个表,查询两个库表最后更新时间 UPDATE_TIME 来判断数据落到那个表中,不必每次执行分库分表算法来查找
SELECT TABLE_NAME, UPDATE_TIME FROM information_schema.TABLES WHERE TABLE_SCHEMA IN ('order_00', 'order_01') ORDER BY TABLE_NAME;
# 转换时间存在'T'
对接 .NET
接口时,对方请求 Json
报文时间存在一个 'T'
public static void main(String[] args) {
try {
String dateStr = "2018-05-15T24:59:59.000+0800";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = simpleDateFormat.parse(dateStr);
System.out.println(date.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "GMT+8")
private Date date;
# MySQL查询当前自然月
更多: MySQL 查询当天、本周,本月、上一个月的数据 (opens new window)
SELECT
*
FROM
t_order g
WHERE
date_format(g.created_time, '%Y-%m') = date_format(now(), '%Y-%m')
# Url转存OSS上传InputStream要传文件大小
更多: 通过url链接将图片上传oss图片显示不完整问题 (opens new window)
String strUrl = "http://xxx.xxx/bf3dd689-bc03-4d28-ae15-xxxxx.jpg";
URL url = new URL(strUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// conn.setConnectTimeout(60 * 1000);
conn.connect();
// 获取图片的实际大小
long contentLength = conn.getContentLengthLong();
// 大小180K
System.out.println(contentLength);
// 通过输入流获取图片数据
InputStream inputStream = conn.getInputStream();
// 大小8K
System.out.println(inputStream.available());
// 图片存储
ossTemplate.putFile(getFileNameFromUrl(strUrl), inputStream, contentLength);
// 下面这种是错误的
ossTemplate.putFile(getFileNameFromUrl(strUrl), inputStream, (long) inputStream.available());
# POI的Workbook转成InputStream及获取大小
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
wb.write(byteArrayOutputStream);
InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
minioTemplate.putFile(filename, inputStream, (long) byteArrayOutputStream.size());
# 上传多个文件加属性
// 原版
@RequestMapping(value = "/file", method = RequestMethod.POST)
public R<?> file(HttpServletRequest request, HttpServletResponse response) throws Exception {
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
// MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
// MultipartHttpServletRequest multipartHttpServletRequest = resolver.resolveMultipart(request);
List<MultipartFile> files = multipartHttpServletRequest.getFiles("files");
String serviceName = request.getParameter("serviceName");
String methodName = request.getParameter("methodName");
String data = request.getParameter("data");
log.debug("base/client - serviceName:{}, methodName:{}, data:{}", serviceName, methodName, data);
SysApiLogDto sysApiLogDto = new SysApiLogDto();
sysApiLogDto.setApiName(serviceName + "/" + methodName);
sysApiLogDto.setRequestTime(new Date());
sysApiLogDto.setReqData(data);
sysApiLogDto.setAppId(CommonConstants.LOG_APP_ID);
sysApiLogDto.setStatus(CommonConstants.LOG_STATUS_SUCCESS);
sysApiLogDto.setReqIp(request.getRemoteAddr());
sysApiLogDto.setBizNo(request.getHeader("userId") + "/" + request.getHeader("token"));
sysApiLogDto.setReqUrl(request.getRequestURI());
final StopWatch stopWatch = new StopWatch();
R<?> result = null;
try {
stopWatch.start();
BaseContextHandler.setIp(request.getRemoteAddr());
BaseContextHandler.setUserId(request.getHeader("userId"));
BaseContextHandler.setToken(request.getHeader("token"));
JSONObject jsonData;
if (StringUtils.isEmpty(data)) {
jsonData = new JSONObject();
} else {
jsonData = JSON.parseObject(data);
}
Map<String, Object> paramMap = jsonData;
paramMap.put("files", files);
Parameter parameter = new Parameter(serviceName, methodName, paramMap);
result = (R<?>) handler.execute(parameter).getResult();
} catch (Exception e) {
log.error("服务异常", e);
sysApiLogDto.setStatus(CommonConstants.LOG_STATUS_FAIL);
sysApiLogDto.setExceptionStack(ExceptionUtil.getExceptionMessage(e));
result = R.restResult(ResultCode.FAIL.getCode(), "服务异常,请稍后再试");
} finally {
BaseContextHandler.remove();
stopWatch.stop();
}
sysApiLogDto.setErrorMsg("耗时:" + stopWatch.getTime() + "ms");
sysApiLogDto.setRespData(result.toString());
// 发送异步日志事件
publisher.publishEvent(new BaseLogEvent(sysApiLogDto));
return result;
}
// 优化
@RequestMapping(value = "/file", method = RequestMethod.POST)
public R<?> file(HttpServletRequest request, HttpServletResponse response,
@RequestParam(value = "files", required = false) List<MultipartFile> files,
@RequestParam(value = "serviceName") String serviceName,
@RequestParam(value = "methodName") String methodName,
@RequestParam(value = "data", required = false) String data,
@RequestHeader(value = "userId", required = false) String userId,
@RequestHeader(value = "token", required = false) String token) throws Exception {
log.debug("base/file - serviceName:{}, methodName:{}, data:{}", serviceName, methodName, data);
SysApiLogDto sysApiLogDto = new SysApiLogDto();
sysApiLogDto.setApiName(serviceName + "/" + methodName);
sysApiLogDto.setRequestTime(new Date());
sysApiLogDto.setReqData(data);
sysApiLogDto.setAppId(CommonConstants.LOG_APP_ID);
sysApiLogDto.setStatus(CommonConstants.LOG_STATUS_SUCCESS);
sysApiLogDto.setReqIp(request.getRemoteAddr());
sysApiLogDto.setBizNo(userId + "/" + token);
sysApiLogDto.setReqUrl(request.getRequestURI());
final StopWatch stopWatch = new StopWatch();
R<?> result = null;
try {
stopWatch.start();
BaseContextHandler.setIp(request.getRemoteAddr());
BaseContextHandler.setUserId(userId);
BaseContextHandler.setToken(token);
JSONObject jsonData;
if (StringUtils.isEmpty(data)) {
jsonData = new JSONObject();
} else {
jsonData = JSON.parseObject(data);
}
Map<String, Object> paramMap = jsonData;
paramMap.put("files", files);
Parameter parameter = new Parameter(serviceName, methodName, paramMap);
result = (R<?>) handler.execute(parameter).getResult();
} catch (Exception e) {
log.error("服务异常", e);
sysApiLogDto.setStatus(CommonConstants.LOG_STATUS_FAIL);
sysApiLogDto.setExceptionStack(ExceptionUtil.getExceptionMessage(e));
result = R.restResult(ResultCode.FAIL.getCode(), "服务异常,请稍后再试");
} finally {
BaseContextHandler.remove();
stopWatch.stop();
}
sysApiLogDto.setErrorMsg("耗时:" + stopWatch.getTime() + "ms");
sysApiLogDto.setRespData(result.toString());
// 发送异步日志事件
publisher.publishEvent(new BaseLogEvent(sysApiLogDto));
return result;
}
# Map转成对象
import org.apache.commons.beanutils.BeanUtils;
BeanUtils.populate(object, map);
# Maven依赖存在一样对象
两个依赖存在同包,同对象名,默认是以 pom 文件引入顺序前面的依赖包里的代码为准
# @Transactional回滚
未往上级抛异常,而且被捕获,不会触发回滚,需要自己进行回滚处理
@PostMapping("/xxx")
@Transactional(rollbackFor = Exception.class)
public String xxx() {
try {
// ...
xxxMapper.insertSelective(xxx);
// throw new RuntimeException();
} catch (Exception e) {
log.error("error");
// Controller中try catch了必须手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return Boolean.FALSE.toString();
}
return Boolean.TRUE.toString();
}
推荐统一走 Service,这样 Service 层往上抛异常就触发事务回滚了,Controller 再去捕获到处理
// Controller
@PostMapping("/xxx")
public String xxx() {
try {
xxxService.xxx();
} catch (Exception e) {
log.error("error");
// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return Boolean.FALSE.toString();
}
return Boolean.TRUE.toString();
}
// Service
@Override
@Transactional(rollbackFor = Exception.class)
public void xxx() {
// ...
xxxMapper.insertSelective(xxx);
// throw new RuntimeException();
}
# Get传递数组
var inputArgs = new Array();
inputArgs[0] = taskId;
inputArgs[1] = documentNo;
inputArgs[2] = print;
inputArgs[3] = endorSeqNoPrint;
inputArgs[4] = serialNo;
inputArgs[5] = "";
inputArgs[6] = companyCode;
inputArgs[7] = "";
inputArgs[8] = rationTypeCode;
var reportName = "print" + inputArgs[1];
var url = "http://xxx"
url = url + "?reportName=" + reportName + "&inputArgs[]=" + inputArgs[0] +
"&inputArgs[]=" + inputArgs[1] + "&inputArgs[]=" + inputArgs[2] + "&inputArgs[]=" + inputArgs[3] +
"&inputArgs[]=" + inputArgs[4] + "&inputArgs[]=" + inputArgs[5] + "&inputArgs[]=" + inputArgs[6] +
"&inputArgs[]=" + inputArgs[7] + "&inputArgs[]=" + inputArgs[8];
String[] inputArgs = request.getParameterValues("inputArgs[]");
# @FeignClient注解接口无法注入
启动类上没有使用 @EnableFeignClients
扫描指定包
解决通过@FeignClient自动注入service失败的问题 (opens new window)
# CSV解析行数据分割问题
// 不能直接使用"|"分割,必须转义使用"\\|"
String[] strs = list.get(i).split("\\|");
# MySQL8.0修改主键问题
MySQL8.0
新特性 sql_require_primary_key
配置,开启后会强制主键检查,表不能没有主键,最后发现可以同时执行删除主键和新增主键的 SQL
命令,如下
-- 去除主键order_main_id的自增AUTO_INCREMENT属性
ALTER TABLE `t_order_endor_main` MODIFY `order_main_id` BIGINT;
-- 设置字段数据递增,默认全部为0,得修改为递增的数据
SET @r := 0;
UPDATE `t_order_endor_main` SET `order_endor_main_id` = (@r :=@r + 1);
-- 删除原有主键,添加order_endor_main_id字段为新主键
ALTER TABLE `t_order_endor_main` DROP PRIMARY KEY, ADD PRIMARY KEY (`order_endor_main_id`);