commit
3b00297fef
|
@ -1,9 +1,9 @@
|
||||||
window.Utils = {
|
class CusUtils {
|
||||||
/**
|
/**
|
||||||
* @author youhong.ai
|
* @author youhong.ai
|
||||||
* @desc 发起请求
|
* @desc 发起请求
|
||||||
*/
|
*/
|
||||||
request: function (url, type = "GET", data, isAsync = true, success = () => {
|
request(url, type = "GET", data, isAsync = true, success = () => {
|
||||||
}, error = () => {
|
}, error = () => {
|
||||||
}, complete = () => {
|
}, complete = () => {
|
||||||
}, contentType = 'application/json', beforeSend = () => {
|
}, contentType = 'application/json', beforeSend = () => {
|
||||||
|
@ -24,13 +24,13 @@ window.Utils = {
|
||||||
options.data = JSON.stringify(data)
|
options.data = JSON.stringify(data)
|
||||||
}
|
}
|
||||||
return $.ajax(options)
|
return $.ajax(options)
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author youhong.ai
|
* @author youhong.ai
|
||||||
* @desc 发起请求
|
* @desc 发起请求
|
||||||
*/
|
*/
|
||||||
api: function (requestOptions = {
|
api(requestOptions = {
|
||||||
url: "",
|
url: "",
|
||||||
type: "GET",
|
type: "GET",
|
||||||
data: "",
|
data: "",
|
||||||
|
@ -61,13 +61,13 @@ window.Utils = {
|
||||||
}
|
}
|
||||||
}, requestOptions)
|
}, requestOptions)
|
||||||
return $.ajax(options)
|
return $.ajax(options)
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author youhong.ai
|
* @author youhong.ai
|
||||||
* @desc 获取react组件实例
|
* @desc 获取react组件实例
|
||||||
*/
|
*/
|
||||||
findReact: function (dom, traverseUp = 0) {
|
findReact(dom, traverseUp = 0) {
|
||||||
const key = Object.keys(dom).find(key => {
|
const key = Object.keys(dom).find(key => {
|
||||||
return key.startsWith("__reactFiber$") // react 17+
|
return key.startsWith("__reactFiber$") // react 17+
|
||||||
|| key.startsWith("__reactInternalInstance$")
|
|| key.startsWith("__reactInternalInstance$")
|
||||||
|
@ -96,7 +96,7 @@ window.Utils = {
|
||||||
compFiber = GetCompFiber(compFiber);
|
compFiber = GetCompFiber(compFiber);
|
||||||
}
|
}
|
||||||
return compFiber.stateNode;
|
return compFiber.stateNode;
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,7 +104,7 @@ window.Utils = {
|
||||||
* @param fieldName 字段名称
|
* @param fieldName 字段名称
|
||||||
* @returns {*|string}
|
* @returns {*|string}
|
||||||
*/
|
*/
|
||||||
convertNameToIdUtil: function (fieldName) {
|
convertNameToIdUtil(fieldName) {
|
||||||
let fieldIds = [];
|
let fieldIds = [];
|
||||||
if (fieldName instanceof Array) {
|
if (fieldName instanceof Array) {
|
||||||
fieldName.forEach(item => {
|
fieldName.forEach(item => {
|
||||||
|
@ -113,19 +113,32 @@ window.Utils = {
|
||||||
return fieldIds.join(',')
|
return fieldIds.join(',')
|
||||||
}
|
}
|
||||||
return Utils.convertNameObjToId(fieldName)
|
return Utils.convertNameObjToId(fieldName)
|
||||||
},
|
}
|
||||||
|
|
||||||
|
getQueryString(name) {
|
||||||
|
let reg = new RegExp("(^|&|\/?)" + name + "=([^&]*)(&|$)", "i");
|
||||||
|
let searchStr = window.location.href
|
||||||
|
if (searchStr.startsWith('&')) {
|
||||||
|
searchStr = searchStr.substr(1)
|
||||||
|
}
|
||||||
|
let search = searchStr.match(reg)
|
||||||
|
if (search != null) {
|
||||||
|
return unescape(search[2]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将字段名称转为字段id
|
* 将字段名称转为字段id
|
||||||
* @param fieldObj 字段名称对象 {string|object}
|
* @param fieldObj 字段名称对象 {string|object}
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
convertNameObjToId: function (fieldObj = {fieldName: '', table: 'main'}) {
|
convertNameObjToId(fieldObj = {fieldName: '', table: 'main'}) {
|
||||||
if (typeof fieldObj === 'object') {
|
if (typeof fieldObj === 'object') {
|
||||||
return WfForm.convertFieldNameToId(fieldObj.fieldName, fieldObj.table)
|
return WfForm.convertFieldNameToId(fieldObj.fieldName, fieldObj.table)
|
||||||
}
|
}
|
||||||
return WfForm.convertFieldNameToId(fieldObj)
|
return WfForm.convertFieldNameToId(fieldObj)
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据字段名称查询字段值
|
* 根据字段名称查询字段值
|
||||||
|
@ -133,17 +146,18 @@ window.Utils = {
|
||||||
* @param rowIndex 明细行下表(明细获取才传递)
|
* @param rowIndex 明细行下表(明细获取才传递)
|
||||||
* @returns {*} 字段值
|
* @returns {*} 字段值
|
||||||
*/
|
*/
|
||||||
getFiledValueByName: function (fieldName, rowIndex) {
|
getFiledValueByName(fieldName, rowIndex) {
|
||||||
return WfForm.getFieldValue(Utils.convertNameObjToId(fieldName) + (rowIndex ? '_' + rowIndex : ''))
|
return WfForm.getFieldValue(Utils.convertNameObjToId(fieldName) + (rowIndex ? '_' + rowIndex : ''))
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过字段名称修改字段值
|
* 通过字段名称修改字段值
|
||||||
* @param fieldName 字段名称
|
* @param fieldName 字段名称
|
||||||
* @param value 值
|
* @param value 值
|
||||||
*/
|
*/
|
||||||
changeFieldValueByName: function (fieldName, value) {
|
changeFieldValueByName(fieldName, value) {
|
||||||
WfForm.changeFieldValue(Utils.convertNameObjToId(fieldName), {value})
|
WfForm.changeFieldValue(Utils.convertNameObjToId(fieldName), {value})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.Utils = new CusUtils()
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
const ecodeSDK = {}
|
||||||
|
ecodeSDK.setCom = (id, name, Com) => {
|
||||||
|
}
|
||||||
|
ecodeSDK.imp = (obj) => {
|
||||||
|
}
|
||||||
|
ecodeSDK.exp = (obj) => {
|
||||||
|
}
|
||||||
const WfForm = {
|
const WfForm = {
|
||||||
isMobile: () => {
|
isMobile: () => {
|
||||||
// true表示是eMobile、微信、钉钉等移动终端,false代表PC端
|
// true表示是eMobile、微信、钉钉等移动终端,false代表PC端
|
||||||
|
|
|
@ -3,49 +3,68 @@ const yjfksj = WfForm.convertFieldNameToId('yjfksj');
|
||||||
// 主表比例
|
// 主表比例
|
||||||
const bl = WfForm.convertFieldNameToId('bcfkbl');
|
const bl = WfForm.convertFieldNameToId('bcfkbl');
|
||||||
// 明细要求付款日期
|
// 明细要求付款日期
|
||||||
const detail3Yqfkrq = WfForm.convertFieldNameToId('yqfkrq', "detail_3");
|
const detail3Yqfkrq = WfForm.convertFieldNameToId('yqfkrq',"detail_3");
|
||||||
// 明细本次付款比例
|
// 明细本次付款比例
|
||||||
const detailBl = WfForm.convertFieldNameToId('bcfkbl', "detail_3");
|
const detailBl = WfForm.convertFieldNameToId('bcfkbl',"detail_3");
|
||||||
// 关联订单
|
// 关联订单
|
||||||
const relationOrder = WfForm.convertFieldNameToId("gldd");
|
const relationOrder = WfForm.convertFieldNameToId("gldd");
|
||||||
// 明细4施工合同字段
|
// 明细4施工合同字段
|
||||||
const detail4SgField = WfForm.convertFieldNameToId("sght", "detail_4");
|
const detail4SgField = WfForm.convertFieldNameToId("sght","detail_4");
|
||||||
// 明细4采购合同字段
|
// 明细4采购合同字段
|
||||||
const detail4CgField = WfForm.convertFieldNameToId("cght", "detail_4");
|
const detail4CgField = WfForm.convertFieldNameToId("cght","detail_4");
|
||||||
// 明细4发票字段
|
// 明细4发票字段
|
||||||
const detail4FpField = WfForm.convertFieldNameToId("fpje", "detail_4");
|
const detail4FpField = WfForm.convertFieldNameToId("fpje","detail_4");
|
||||||
// 明细4合同发票金额和
|
// 明细4合同发票金额和
|
||||||
let detail4Map = new Map();
|
let detail4Map = new Map();
|
||||||
// 明细3施工合同字段
|
// 明细3施工合同字段
|
||||||
const detail3SgField = WfForm.convertFieldNameToId("sght", "detail_3");
|
const detail3SgField = WfForm.convertFieldNameToId("sght","detail_3");
|
||||||
// 明细3采购合同字段
|
// 明细3采购合同字段
|
||||||
const detail3CgField = WfForm.convertFieldNameToId("cght", "detail_3");
|
const detail3CgField = WfForm.convertFieldNameToId("cght","detail_3");
|
||||||
// 明细3 含税金额
|
// 明细3 含税金额
|
||||||
const detail3TaxField = WfForm.convertFieldNameToId("ddhsje", "detail_3");
|
const detail3TaxField = WfForm.convertFieldNameToId("ddhsje","detail_3");
|
||||||
// 明细3以收票金额
|
// 明细3以收票金额
|
||||||
const detail3YspField = WfForm.convertFieldNameToId("yspje", "detail_3");
|
const detail3YspField = WfForm.convertFieldNameToId("yspje","detail_3");
|
||||||
|
|
||||||
|
const detail3ApplyMoneyField = WfForm.convertFieldNameToId("bcfkje", "detail_3");
|
||||||
|
const detail3CgId = WfForm.convertFieldNameToId("cgddzbid", "detail_3");
|
||||||
|
const detail3Bl = WfForm.convertFieldNameToId('bcfkbl', "detail_3");
|
||||||
|
let detailBlMap = new Map();
|
||||||
|
const detail1Bl = WfForm.convertFieldNameToId('bcljfkbl', "detail_1");
|
||||||
|
const detail1CgId = WfForm.convertFieldNameToId("cgddzbid", "detail_1");
|
||||||
|
|
||||||
WfForm.registerCheckEvent(WfForm.OPER_ADDROW + "3", function (callback) {
|
WfForm.registerCheckEvent(WfForm.OPER_ADDROW + "3", function (callback) {
|
||||||
callback();
|
callback();
|
||||||
setTimeout(() => {
|
setTimeout(()=>{
|
||||||
initDeatail3Date();
|
initDeatail3Date();
|
||||||
}, 5)
|
},5)
|
||||||
});
|
});
|
||||||
// 关联订单变化时将明细四的合同-金额map进行初始化
|
// // 关联订单变化时将明细四的合同-金额map进行初始化
|
||||||
|
// WfForm.bindFieldChangeEvent(relationOrder, function(obj,id,value){
|
||||||
|
// setTimeout(()=>{
|
||||||
|
// initDetail4Map();
|
||||||
|
// changeDetail3SpMoney();
|
||||||
|
// },2000);
|
||||||
|
// });
|
||||||
|
|
||||||
WfForm.bindFieldChangeEvent(relationOrder, function (obj, id, value) {
|
WfForm.bindFieldChangeEvent(relationOrder, function (obj, id, value) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
initDetail4Map();
|
initDetail4Map();
|
||||||
changeDetail3SpMoney();
|
changeDetail3SpMoney();
|
||||||
|
changeDetail1ApplyAMount();
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
WfForm.bindDetailFieldChangeEvent(detail3ApplyMoneyField, function () {
|
||||||
|
setTimeout(() => {
|
||||||
|
changeDetail1ApplyAMount();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
WfForm.bindFieldChangeEvent(`${yjfksj},${bl}`, function(obj,id,value){
|
||||||
WfForm.bindFieldChangeEvent(`${yjfksj},${bl}`, function (obj, id, value) {
|
|
||||||
initDeatail3Date();
|
initDeatail3Date();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算明细三开票金额
|
// 计算明细三开票金额
|
||||||
function changeDetail3SpMoney() {
|
function changeDetail3SpMoney(){
|
||||||
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||||
for (let i = 0; i < detail3RowArr.length; i++) {
|
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||||
let rowIndex = detail3RowArr[i];
|
let rowIndex = detail3RowArr[i];
|
||||||
|
@ -55,49 +74,49 @@ function changeDetail3SpMoney() {
|
||||||
let fpje = detail4Map.get(detail3SgFieldVal) ?? detail4Map.get(detail3CgFieldVal);
|
let fpje = detail4Map.get(detail3SgFieldVal) ?? detail4Map.get(detail3CgFieldVal);
|
||||||
let mapVal = detail4Map.get(detail3SgFieldVal);
|
let mapVal = detail4Map.get(detail3SgFieldVal);
|
||||||
let key = detail3SgFieldVal;
|
let key = detail3SgFieldVal;
|
||||||
if (!mapVal) {
|
if(!mapVal){
|
||||||
key = detail3CgFieldVal;
|
key = detail3CgFieldVal;
|
||||||
}
|
}
|
||||||
console.log('key ', key);
|
console.log('key ', key);
|
||||||
if (fpje) {
|
if(fpje){
|
||||||
console.log('fpje => ', fpje);
|
console.log('fpje => ', fpje);
|
||||||
// 0.1 0.3
|
// 0.1 0.3
|
||||||
let detail3TaxVal = WfForm.getFieldValue(`${detail3TaxField}_${rowIndex}`);
|
let detail3TaxVal = WfForm.getFieldValue(`${detail3TaxField}_${rowIndex}`);
|
||||||
console.log('detail3TaxVal => ', detail3TaxVal);
|
console.log('detail3TaxVal => ', detail3TaxVal);
|
||||||
let tempJe = fpje - detail3TaxVal;
|
let tempJe = fpje - detail3TaxVal;
|
||||||
console.log('tempJe => ', tempJe);
|
console.log('tempJe => ', tempJe);
|
||||||
if (fpje > detail3TaxVal) {
|
if(fpje > detail3TaxVal){
|
||||||
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`, {value: detail3TaxVal});
|
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`,{value: detail3TaxVal});
|
||||||
} else if (tempJe <= 0 && fpje == 0) {
|
}else if(tempJe <= 0 && fpje == 0){
|
||||||
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`, {value: 0});
|
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`,{value: 0});
|
||||||
} else if (tempJe < 0) {
|
}else if(tempJe < 0){
|
||||||
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`, {value: fpje});
|
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`,{value: fpje});
|
||||||
tempJe = 0;
|
tempJe = 0;
|
||||||
}
|
}
|
||||||
detail4Map.set(key, tempJe);
|
detail4Map.set(key, tempJe);
|
||||||
console.log('detail4Map ', detail4Map)
|
console.log('detail4Map ', detail4Map)
|
||||||
} else {
|
}else{
|
||||||
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`, {value: 0});
|
WfForm.changeFieldValue(`${detail3YspField}_${rowIndex}`,{value: 0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function initDeatail3Date() {
|
function initDeatail3Date(){
|
||||||
let dateVal = WfForm.getFieldValue(yjfksj);
|
let dateVal = WfForm.getFieldValue(yjfksj);
|
||||||
let blVal = WfForm.getFieldValue(bl);
|
let blVal = WfForm.getFieldValue(bl);
|
||||||
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||||
for (let i = 0; i < detail3RowArr.length; i++) {
|
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||||
let rowIndex = detail3RowArr[i];
|
let rowIndex = detail3RowArr[i];
|
||||||
if (rowIndex !== "") {
|
if (rowIndex !== "") {
|
||||||
WfForm.changeFieldValue(`${detail3Yqfkrq}_${rowIndex}`, {value: dateVal});
|
WfForm.changeFieldValue(`${detail3Yqfkrq}_${rowIndex}`,{value: dateVal});
|
||||||
WfForm.changeFieldValue(`${detailBl}_${rowIndex}`, {value: blVal});
|
WfForm.changeFieldValue(`${detailBl}_${rowIndex}`,{value: blVal});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initDetail4Map() {
|
function initDetail4Map(){
|
||||||
let detail4RowArr = WfForm.getDetailAllRowIndexStr('detail_4').split(",");
|
let detail4RowArr = WfForm.getDetailAllRowIndexStr('detail_4').split(",");
|
||||||
console.log('detail4RowArr ', detail4RowArr);
|
console.log('detail4RowArr ', detail4RowArr);
|
||||||
for (let i = 0; i < detail4RowArr.length; i++) {
|
for (let i = 0; i < detail4RowArr.length; i++) {
|
||||||
|
@ -110,7 +129,7 @@ function initDetail4Map() {
|
||||||
console.log('detail4SgFieldVal ', detail4SgFieldVal);
|
console.log('detail4SgFieldVal ', detail4SgFieldVal);
|
||||||
console.log('detail4CgFieldVal ', detail4CgFieldVal);
|
console.log('detail4CgFieldVal ', detail4CgFieldVal);
|
||||||
console.log('detail4FpFieldVal ', detail4FpFieldVal);
|
console.log('detail4FpFieldVal ', detail4FpFieldVal);
|
||||||
if (!detail4FpFieldVal) {
|
if(!detail4FpFieldVal){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
key = detail4CgFieldVal ?? detail4SgFieldVal;
|
key = detail4CgFieldVal ?? detail4SgFieldVal;
|
||||||
|
@ -123,37 +142,121 @@ function initDetail4Map() {
|
||||||
console.log('detail4Map ', detail4Map)
|
console.log('detail4Map ', detail4Map)
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail3ApplyMoneyField = WfForm.convertFieldNameToId("bcfkje", "detail_3");
|
|
||||||
const detail3CgId = WfForm.convertFieldNameToId("cgddzbid", "detail_3");
|
|
||||||
const detail3Bl = WfForm.convertFieldNameToId('bcfkbl', "detail_3");
|
|
||||||
let detailBlMap = new Map();
|
|
||||||
const detail1Bl = WfForm.convertFieldNameToId('fkbl', "detail_1");
|
|
||||||
const detail1CgId = WfForm.convertFieldNameToId("cgddzbid", "detail_1");
|
|
||||||
WfForm.bindDetailFieldChangeEvent(detail3ApplyMoneyField, (id, rowIndex, value) => {
|
WfForm.bindDetailFieldChangeEvent(detail3ApplyMoneyField, (id, rowIndex, value) => {
|
||||||
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
setTimeout(()=>{
|
||||||
detailBlMap = new Map();
|
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||||
for (let i = 0; i < detail3RowArr.length; i++) {
|
detailBlMap = new Map();
|
||||||
let rowIndex = detail3RowArr[i];
|
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||||
if (rowIndex !== "") {
|
let rowIndex = detail3RowArr[i];
|
||||||
let bl = parseFloat(WfForm.getFieldValue(`${detail3Bl}_${rowIndex}`));
|
if (rowIndex !== "") {
|
||||||
let cg = WfForm.getFieldValue(`${detail3CgId}_${rowIndex}`);
|
let blStr = WfForm.getFieldValue(`${detail3Bl}_${rowIndex}`);
|
||||||
let mapVal = parseFloat(detailBlMap.get(cg) ?? 0) + bl;
|
if(blStr == ''){
|
||||||
detailBlMap.set(cg, mapVal);
|
console.log('111')
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let bl = parseFloat(blStr);
|
||||||
|
console.log('bl => ' , bl);
|
||||||
|
let cg = WfForm.getFieldValue(`${detail3CgId}_${rowIndex}`);
|
||||||
|
let mapVal = detailBlMap.get(cg);
|
||||||
|
if(!mapVal){
|
||||||
|
mapVal = 0;
|
||||||
|
}
|
||||||
|
mapVal+= bl;
|
||||||
|
detailBlMap.set(cg, mapVal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
console.log('detailBlMap ', detailBlMap)
|
||||||
console.log('detailBlMap ', detailBlMap)
|
let detail1RowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||||
let detail1RowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
for (let i = 0; i < detail1RowArr.length; i++) {
|
||||||
for (let i = 0; i < detail1RowArr.length; i++) {
|
let rowIndex = detail1RowArr[i];
|
||||||
let rowIndex = detail1RowArr[i];
|
if (rowIndex !== "") {
|
||||||
if (rowIndex !== "") {
|
let cg = WfForm.getFieldValue(`${detail1CgId}_${rowIndex}`);
|
||||||
let cg = WfForm.getFieldValue(`${detail1CgId}_${rowIndex}`);
|
let val = detailBlMap.get(cg) === '' ? 0 : detailBlMap.get(cg);
|
||||||
let val = detailBlMap.get(cg) === '' ? 0 : detailBlMap.get(cg);
|
WfForm.changeFieldValue(`${detail1Bl}_${rowIndex}`, {value: val})
|
||||||
WfForm.changeFieldValue(`${detail1Bl}_${rowIndex}`, {value: val})
|
}
|
||||||
}
|
}
|
||||||
}
|
},300);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 明细3合同总金额
|
||||||
|
const detail3AmountField = WfForm.convertFieldNameToId("htzje", "detail_3");
|
||||||
|
// 明细3订单号字段
|
||||||
|
const detail3OrderField = WfForm.convertFieldNameToId("ddh","detail_3");
|
||||||
|
// 明细1本次付款金额字段
|
||||||
|
const detail1ApplyAmountField = WfForm.convertFieldNameToId("bcsqfkje","detail_1");
|
||||||
|
// 明细1含税金额
|
||||||
|
const detail1AmountTaxField = WfForm.convertFieldNameToId("ddmxxj","detail_1");
|
||||||
|
// 明细1订单号
|
||||||
|
const detail1OrderField = WfForm.convertFieldNameToId("ddh","detail_1");
|
||||||
|
// 明细1预计付款日期
|
||||||
|
const detail1ApplyDate = WfForm.convertFieldNameToId("yjfkrq","detail_1");
|
||||||
|
function changeDetail1ApplyAMount(){
|
||||||
|
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||||
|
// 明细3 订单号-合同总金额map
|
||||||
|
let detail3ContractAmountMap = new Map();
|
||||||
|
let detail3ApplyAmountMap = new Map();
|
||||||
|
let detail3ApplyMoneyField = WfForm.convertFieldNameToId("bcfkje", "detail_3");
|
||||||
|
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||||
|
let rowIndex = detail3RowArr[i];
|
||||||
|
if (rowIndex !== "") {
|
||||||
|
let detail3Order = WfForm.getFieldValue(`${detail3OrderField}_${rowIndex}`);
|
||||||
|
if(!detail3ContractAmountMap.has(detail3Order)){
|
||||||
|
detail3ContractAmountMap.set(detail3Order, parseFloat(WfForm.getFieldValue(`${detail3AmountField}_${rowIndex}`)));
|
||||||
|
}
|
||||||
|
let detail3Amount = parseFloat(WfForm.getFieldValue(`${detail3ApplyMoneyField}_${rowIndex}`));
|
||||||
|
let sum = detail3ApplyAmountMap.get(detail3Order) ?? 0;
|
||||||
|
detail3ApplyAmountMap.set(detail3Order, sum + detail3Amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('detail3ContractAmountMap : ', detail3ContractAmountMap);
|
||||||
|
console.log('detail3ApplyAmountMap : ', detail3ApplyAmountMap);
|
||||||
|
let detail1RowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||||
|
for (let i = 0; i < detail1RowArr.length; i++) {
|
||||||
|
let rowIndex = detail1RowArr[i];
|
||||||
|
if (rowIndex !== "") {
|
||||||
|
let detail1Order =WfForm.getFieldValue(`${detail1OrderField}_${rowIndex}`);
|
||||||
|
console.log('detail1Order ', detail1Order);
|
||||||
|
// 含税金额
|
||||||
|
let detail1AmountTax = parseFloat(WfForm.getFieldValue(`${detail1AmountTaxField}_${rowIndex}`) ?? 0);
|
||||||
|
console.log('detail1AmountTax ', detail1AmountTax);
|
||||||
|
// 本次付款金额和
|
||||||
|
let applySum = parseFloat(detail3ApplyAmountMap.get(detail1Order) ?? 0);
|
||||||
|
console.log('applySum ', applySum);
|
||||||
|
// 合同总金额
|
||||||
|
let contractAmount = parseFloat(detail3ContractAmountMap.get(detail1Order) ?? 0);
|
||||||
|
console.log('contractAmount ', contractAmount);
|
||||||
|
let val = (applySum / contractAmount) * detail1AmountTax;
|
||||||
|
console.log('计算金额 : ', val)
|
||||||
|
WfForm.changeFieldValue(`${detail1ApplyAmountField}_${rowIndex}`, {value: val})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WfForm.bindDetailFieldChangeEvent(detail3Yqfkrq,()=>{
|
||||||
|
initDetail1ApplyDate();
|
||||||
|
});
|
||||||
|
function initDetail1ApplyDate(){
|
||||||
|
let detail3RowArr = WfForm.getDetailAllRowIndexStr('detail_3').split(",");
|
||||||
|
let detail3DateMap = new Map();
|
||||||
|
for (let i = 0; i < detail3RowArr.length; i++) {
|
||||||
|
let rowIndex = detail3RowArr[i];
|
||||||
|
if (rowIndex !== "") {
|
||||||
|
let detail3Order = WfForm.getFieldValue(`${detail3OrderField}_${rowIndex}`);
|
||||||
|
if(!detail3DateMap.has(detail3Order)){
|
||||||
|
detail3DateMap.set(detail3Order, WfForm.getFieldValue(`${detail3Yqfkrq}_${rowIndex}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('detail3DateMap ', detail3DateMap);
|
||||||
|
let detail1RowArr = WfForm.getDetailAllRowIndexStr('detail_1').split(",");
|
||||||
|
for (let i = 0; i < detail1RowArr.length; i++) {
|
||||||
|
let rowIndex = detail1RowArr[i];
|
||||||
|
if (rowIndex !== "") {
|
||||||
|
let detail1Order =WfForm.getFieldValue(`${detail1OrderField}_${rowIndex}`);
|
||||||
|
WfForm.changeFieldValue(`${detail1ApplyDate}_${rowIndex}`, {value: detail3DateMap.get(detail1Order)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 首台销售日期
|
||||||
|
const firstSaleDateField = WfForm.convertFieldNameToId("stxsrq");
|
||||||
|
// 跟踪时间
|
||||||
|
const trackTimeField = WfForm.convertFieldNameToId("gzsj");
|
||||||
|
// 跟踪时间为三个月以内
|
||||||
|
const threeMonthIndex = 1;
|
||||||
|
// 是否提交等待节点
|
||||||
|
const submitWaitNode = WfForm.convertFieldNameToId("sftjddjd");
|
||||||
|
// 下次超时提醒日期
|
||||||
|
const timeoutRemindDateFiled = WfForm.convertFieldNameToId("cstxrq");
|
||||||
|
// 跟踪天数
|
||||||
|
const trackingDays = WfForm.getFieldValue(trackTimeField) <= 1 ? 15 : 30;
|
||||||
|
// 跟踪触发行数
|
||||||
|
const trackingLineField = WfForm.convertFieldNameToId("gzcfxs");
|
||||||
|
$(() => {
|
||||||
|
let detail2LineNum = WfForm.getDetailRowCount("detail_2");
|
||||||
|
// let firstTrack = Boolean(true);
|
||||||
|
// if (new Date(firstSaleDate) < new Date(currentDate) && dayDiff > 0) {
|
||||||
|
// firstTrack = false;
|
||||||
|
// }
|
||||||
|
// console.log('firstTrack ', firstTrack)
|
||||||
|
// 到达节点次数
|
||||||
|
const nodeNum = getNodeNum();
|
||||||
|
let trackingLine = parseInt(WfForm.getFieldValue(trackingLineField));
|
||||||
|
// 如果不是则自动添加一行明细让他自己填写
|
||||||
|
if (detail2LineNum < trackingLine && detail2LineNum < nodeNum) {
|
||||||
|
console.log('添加一行明细!');
|
||||||
|
WfForm.addDetailRow("detail_2", {});
|
||||||
|
}
|
||||||
|
if(detail2LineNum >= trackingLine){
|
||||||
|
WfForm.changeFieldValue(submitWaitNode, {value: 1});
|
||||||
|
WfForm.changeFieldValue(timeoutRemindDateFiled, {value: ''});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initTimeoutDate();
|
||||||
|
WfForm.bindFieldChangeEvent(`${firstSaleDateField},${trackTimeField}`,()=>{
|
||||||
|
initTimeoutDate();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getNodeNum(){
|
||||||
|
let firstSaleDate = WfForm.getFieldValue(firstSaleDateField);
|
||||||
|
console.log('首台销售日期 ', firstSaleDate);
|
||||||
|
let currentDate = getCurrentDate();
|
||||||
|
let dayDiff = getDaysDiff(firstSaleDate, currentDate);
|
||||||
|
console.log('当前天数与首台销售日期相差天数 : ', dayDiff)
|
||||||
|
return Math.floor(dayDiff / trackingDays) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTimeoutDate(){
|
||||||
|
console.log('==== initTimeoutDate begin ====')
|
||||||
|
let firstSaleDate = WfForm.getFieldValue(firstSaleDateField);
|
||||||
|
const nodeNum = getNodeNum();
|
||||||
|
console.log('到达节点次数 ', nodeNum);
|
||||||
|
console.log('跟踪天数 ', trackingDays);
|
||||||
|
let computeTimeoutDate = addDays(firstSaleDate, nodeNum * trackingDays);
|
||||||
|
console.log('计算下次超时日期 ', computeTimeoutDate);
|
||||||
|
let trackingLine = parseInt(WfForm.getFieldValue(trackingLineField));
|
||||||
|
let detail2LineNum = WfForm.getDetailRowCount("detail_2");
|
||||||
|
setTimeout(()=>{
|
||||||
|
WfForm.changeFieldValue(timeoutRemindDateFiled, {value: computeTimeoutDate});
|
||||||
|
// 判断流程提交走向
|
||||||
|
console.log('主表跟踪触发行数 : ', trackingLine)
|
||||||
|
if (nodeNum >= trackingLine) {
|
||||||
|
console.log('nodeNum >= trackingLine');
|
||||||
|
WfForm.changeFieldValue(submitWaitNode, {value: 1});
|
||||||
|
WfForm.changeFieldValue(timeoutRemindDateFiled, {value: ''});
|
||||||
|
WfForm.registerCheckEvent(WfForm.OPER_SUBMIT, function (callback) {
|
||||||
|
detail2LineNum = WfForm.getDetailRowCount("detail_2");
|
||||||
|
if (detail2LineNum < trackingLine) {
|
||||||
|
WfForm.showMessage('请填写明细表信息!');
|
||||||
|
detail2LineNum = WfForm.getDetailRowCount("detail_2");
|
||||||
|
for (let i = 0; i < trackingLine - parseInt(detail2LineNum); i++) {
|
||||||
|
WfForm.addDetailRow("detail_2", {});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
WfForm.changeFieldValue(submitWaitNode, {value: 0});
|
||||||
|
}
|
||||||
|
console.log('==== initTimeoutDate end ====')
|
||||||
|
},10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDaysDiff(date1, date2) {
|
||||||
|
date1 = new Date(date1);
|
||||||
|
date2 = new Date(date2);
|
||||||
|
const oneDay = 24 * 60 * 60 * 1000; // 一天的毫秒数
|
||||||
|
const timeDiff = Math.abs(date1.getTime() - date2.getTime()); // 两个日期对象的毫秒数差值
|
||||||
|
// 将毫秒数差值转换为天数差值并四舍五入
|
||||||
|
return Math.round(timeDiff / oneDay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentDate() {
|
||||||
|
return parseDate(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDate(date) {
|
||||||
|
const currentYear = date.getFullYear();
|
||||||
|
const currentMonth = date.getMonth() + 1; // getMonth()返回0~11,需要加1
|
||||||
|
const currentDay = date.getDate();
|
||||||
|
return currentYear + '-' + currentMonth + '-' + currentDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDays(date, days) {
|
||||||
|
const newDate = new Date(date);
|
||||||
|
newDate.setDate(newDate.getDate() + days);
|
||||||
|
console.log('newDate ', newDate)
|
||||||
|
return parseDate(newDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -680,4 +680,62 @@ $(() => {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/* ******************* 明细数据数量统计添加 end ******************* */
|
/* ******************* 明细数据数量统计添加 end ******************* */
|
||||||
|
|
||||||
|
|
||||||
|
/* ******************* 计算年月日 start ******************* */
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
const config = [{
|
||||||
|
// 源字段
|
||||||
|
sourceField: '',
|
||||||
|
// 目标字段
|
||||||
|
targetField: '',
|
||||||
|
// 日期加月份字段
|
||||||
|
numberField: ''
|
||||||
|
}, {
|
||||||
|
sourceField: '',
|
||||||
|
targetField: '',
|
||||||
|
numberField: ''
|
||||||
|
}]
|
||||||
|
|
||||||
|
runJs();
|
||||||
|
|
||||||
|
function runJs() {
|
||||||
|
config.forEach(item => {
|
||||||
|
bindAction(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindAction(configItem) {
|
||||||
|
let fieldId = WfForm.convertFieldNameToId(configItem.numberField)
|
||||||
|
WfForm.bindFieldChangeEvent(fieldId, (obj, id, value) => {
|
||||||
|
if ("" == value) {
|
||||||
|
WfForm.changeFieldValue(WfForm.convertFieldNameToId(configItem.targetField, {value: ""}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let sourceValue = WfForm.getFieldValue(WfForm.convertFieldNameToId(configItem.sourceField));
|
||||||
|
let date = new Date(sourceValue)
|
||||||
|
date.setMonth(date.getMonth() + +value)
|
||||||
|
let objectDate = new Date();
|
||||||
|
|
||||||
|
|
||||||
|
let day = objectDate.getDate();
|
||||||
|
let month = objectDate.getMonth() + 1;
|
||||||
|
let year = objectDate.getFullYear();
|
||||||
|
WfForm.changeFieldValue(WfForm.convertFieldNameToId(configItem.targetField, {
|
||||||
|
value: `${year}-${fullNum(month)}-${fullNum(day)}`
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullNum(i) {
|
||||||
|
if (i <= 9) {
|
||||||
|
return '0' + i
|
||||||
|
} else {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
/* ******************* 计算年月日 end ******************* */
|
|
@ -0,0 +1,25 @@
|
||||||
|
package aiyh.utils;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.org.apache.commons.jexl3.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>脚本工具</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2023/3/3 23:03</p>
|
||||||
|
*
|
||||||
|
* @author youHong.ai
|
||||||
|
*/
|
||||||
|
public class ScriptUtil {
|
||||||
|
private static final JexlEngine jexl = new JexlBuilder().create();
|
||||||
|
|
||||||
|
public static Object invokeScript(String script, Map<String, Object> params) {
|
||||||
|
JexlContext jc = new MapContext();
|
||||||
|
for (Map.Entry<String, Object> entry : params.entrySet()) {
|
||||||
|
jc.set(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
JexlExpression expression = jexl.createExpression(script);
|
||||||
|
return expression.evaluate(jc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3751,6 +3751,32 @@ public class Util extends weaver.general.Util {
|
||||||
return pathParamMap;
|
return pathParamMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T getClassInstance(String classPath, Class<T> t) {
|
||||||
|
Class<?> aClass;
|
||||||
|
try {
|
||||||
|
aClass = Class.forName(classPath);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IllegalArgumentException("未能找到自定义接口:" + classPath);
|
||||||
|
}
|
||||||
|
if (!t.isAssignableFrom(aClass)) {
|
||||||
|
throw new IllegalArgumentException("自定义接口:" + classPath + " 不是"
|
||||||
|
+ t.getName() + "的子类或实现类!");
|
||||||
|
}
|
||||||
|
Constructor<?> constructor;
|
||||||
|
try {
|
||||||
|
constructor = aClass.getConstructor();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new IllegalArgumentException(classPath + "没有空参构造方法,无法获取构造方法对象!");
|
||||||
|
}
|
||||||
|
T o;
|
||||||
|
try {
|
||||||
|
o = (T) constructor.newInstance();
|
||||||
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalArgumentException("无法构造" + classPath + "对象!");
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
public static Object executeActionProcess(String process, RequestInfo requestInfo) {
|
public static Object executeActionProcess(String process, RequestInfo requestInfo) {
|
||||||
if (StringUtils.isNullOrEmpty(process)) {
|
if (StringUtils.isNullOrEmpty(process)) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -32,6 +32,7 @@ import weaver.file.ImageFileManager;
|
||||||
|
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -127,7 +128,11 @@ public class HttpUtils {
|
||||||
builder.append("&");
|
builder.append("&");
|
||||||
builder.append(entry.getKey());
|
builder.append(entry.getKey());
|
||||||
builder.append("=");
|
builder.append("=");
|
||||||
builder.append(entry.getValue());
|
try {
|
||||||
|
builder.append(URLEncoder.encode(Util.null2String(entry.getValue()), "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
builder.append(entry.getKey());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return removeSeparator(builder);
|
return removeSeparator(builder);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package aiyh.utils.recordset;
|
||||||
|
|
||||||
|
import aiyh.utils.excention.CustomerException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>构建sql</h1>
|
||||||
|
*
|
||||||
|
* <p>create: 2023/5/12 20:51</p>
|
||||||
|
*
|
||||||
|
* @author youHong.ai
|
||||||
|
*/
|
||||||
|
public class MapperBuilderSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>构建更新sql</h2>
|
||||||
|
*
|
||||||
|
* @param table 表名称
|
||||||
|
* @param param 参数
|
||||||
|
* @return 构建的sql
|
||||||
|
*/
|
||||||
|
public static String builderUpdateSql(String table, Map<String, Object> param) {
|
||||||
|
return builderUpdateSql(table, param, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>构建更新sql</h2>
|
||||||
|
*
|
||||||
|
* @param table 表名称
|
||||||
|
* @param param 参数
|
||||||
|
* @return 构建的sql
|
||||||
|
*/
|
||||||
|
public static String builderUpdateSql(String table, Map<String, Object> param, String paramPrefix) {
|
||||||
|
if (StrUtil.isBlank(table) || CollectionUtil.isEmpty(param)) {
|
||||||
|
throw new CustomerException("tableName or param can not to be null!");
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("update ").append(table).append(" set ");
|
||||||
|
for (Map.Entry<String, Object> entry : param.entrySet()) {
|
||||||
|
sb.append(" ")
|
||||||
|
.append(entry.getKey())
|
||||||
|
.append(" = ")
|
||||||
|
.append("#{");
|
||||||
|
if (StrUtil.isNotBlank(paramPrefix)) {
|
||||||
|
sb.append(paramPrefix).append(".");
|
||||||
|
}
|
||||||
|
sb.append(entry.getKey())
|
||||||
|
.append("}")
|
||||||
|
.append(",");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>构建插入sql</h2>
|
||||||
|
*
|
||||||
|
* @param table 表名称
|
||||||
|
* @param param 参数
|
||||||
|
* @return 构建的sql
|
||||||
|
*/
|
||||||
|
public static String builderInsertSql(String table, Map<String, Object> param) {
|
||||||
|
return builderInsertSql(table, param, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String builderInsertSql(String table, Map<String, Object> param, String paramPrefix) {
|
||||||
|
if (StrUtil.isBlank(table) || CollectionUtil.isEmpty(param)) {
|
||||||
|
throw new CustomerException("tableName or param can not to be null!");
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
StringBuilder sbValue = new StringBuilder();
|
||||||
|
sb.append("insert into ").append(table).append(" (");
|
||||||
|
sbValue.append(") values ( ");
|
||||||
|
for (Map.Entry<String, Object> entry : param.entrySet()) {
|
||||||
|
sb.append(entry.getKey()).append(" ,");
|
||||||
|
sbValue.append(" #{");
|
||||||
|
if (StrUtil.isNotBlank(paramPrefix)) {
|
||||||
|
sbValue.append(paramPrefix).append(".");
|
||||||
|
}
|
||||||
|
sbValue.append(entry.getKey()).append("},");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
sbValue.deleteCharAt(sbValue.length() - 1);
|
||||||
|
sb.append(sbValue).append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String builderWhereAnd(Map<String, Object> param, boolean containsWhere) {
|
||||||
|
return builderWhereAnd(param, "whereParam", containsWhere);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String builderWhereAnd(Map<String, Object> param) {
|
||||||
|
return builderWhereAnd(param, "whereParam", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String builderNoWhereAndEn(Map<String, Object> param, boolean containsWhere) {
|
||||||
|
return builderWhereAnd(param, "", containsWhere);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String builderNoWhereAndEn(Map<String, Object> param) {
|
||||||
|
return builderWhereAnd(param, "", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>构建and条件</h2>
|
||||||
|
*
|
||||||
|
* @param param 参数
|
||||||
|
* @param wherePrefix 条件拼接前缀
|
||||||
|
* @param containsWhere 是否包含where
|
||||||
|
* @return 构建的where条件
|
||||||
|
*/
|
||||||
|
public static String builderWhereAnd(Map<String, Object> param, String wherePrefix, boolean containsWhere) {
|
||||||
|
if (CollectionUtil.isEmpty(param)) {
|
||||||
|
throw new CustomerException("tableName or param can not to be null!");
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (containsWhere) {
|
||||||
|
sb.append(" where ");
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> entry : param.entrySet()) {
|
||||||
|
sb.append(entry.getKey()).append(" = #{");
|
||||||
|
if (StrUtil.isNotBlank(wherePrefix)) {
|
||||||
|
sb.append(wherePrefix).append(".");
|
||||||
|
}
|
||||||
|
sb.append(entry.getKey()).append("}").append(" and ");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length() - 5);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -460,7 +460,7 @@ public class ResultMapper {
|
||||||
}
|
}
|
||||||
if ("int".equalsIgnoreCase(columnType) || "long".equalsIgnoreCase(columnType) || "number".equalsIgnoreCase(columnType) || "MEDIUMINT".equalsIgnoreCase(columnType) || "TINYINT".equalsIgnoreCase(columnType) || "SMALLINT".equalsIgnoreCase(columnType) || "BIGINT".equalsIgnoreCase(columnType) || "INTEGER".equalsIgnoreCase(columnType)) {
|
if ("int".equalsIgnoreCase(columnType) || "long".equalsIgnoreCase(columnType) || "number".equalsIgnoreCase(columnType) || "MEDIUMINT".equalsIgnoreCase(columnType) || "TINYINT".equalsIgnoreCase(columnType) || "SMALLINT".equalsIgnoreCase(columnType) || "BIGINT".equalsIgnoreCase(columnType) || "INTEGER".equalsIgnoreCase(columnType)) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getInt(columnName[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
@ -473,11 +473,11 @@ public class ResultMapper {
|
||||||
}
|
}
|
||||||
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
if ("FLOAT".equalsIgnoreCase(columnType) || "DOUBLE".equalsIgnoreCase(columnType) || "DECIMAL".equalsIgnoreCase(columnType)) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(columnName[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(columnName[i]));
|
||||||
}
|
}
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toLowerCase(), rs.getString(i + 1));
|
||||||
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(columnName[i].toUpperCase(), rs.getString(i + 1));
|
||||||
|
@ -507,7 +507,7 @@ public class ResultMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (enable) {
|
if (enable) {
|
||||||
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(i + 1));
|
((Map<? super Object, ? super Object>) o).put(Util.toCamelCase(columnName[i]), rs.getString(columnName[i]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
if (DBConstant.DB_TYPE_ORACLE.equals(rs.getDBType())) {
|
||||||
|
@ -569,7 +569,9 @@ public class ResultMapper {
|
||||||
cassociationValue = paramType.get(declaredField.getType()).apply(String.valueOf(cassociationValue));
|
cassociationValue = paramType.get(declaredField.getType()).apply(String.valueOf(cassociationValue));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue);
|
if (Objects.nonNull(cassociationValue)) {
|
||||||
|
propertyDescriptor.getWriteMethod().invoke(o, cassociationValue);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
@ -584,7 +586,9 @@ public class ResultMapper {
|
||||||
if (collectionMapping != null) {
|
if (collectionMapping != null) {
|
||||||
Object collection = collection(rs, collectionMapping, method);
|
Object collection = collection(rs, collectionMapping, method);
|
||||||
try {
|
try {
|
||||||
propertyDescriptor.getWriteMethod().invoke(o, collection);
|
if (Objects.nonNull(value)) {
|
||||||
|
propertyDescriptor.getWriteMethod().invoke(o, collection);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
Util.getLogger().error("实体数据写入报错:" + fieldName + " => " + value);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
|
@ -647,7 +651,6 @@ public class ResultMapper {
|
||||||
Association[] mappings = annotation.value();
|
Association[] mappings = annotation.value();
|
||||||
Association mapping = null;
|
Association mapping = null;
|
||||||
for (Association item : mappings) {
|
for (Association item : mappings) {
|
||||||
Util.getLogger().info("column: " + item.column() + " ===property: " + item.property() + " ====fieldName: " + filedName);
|
|
||||||
String property = isMap ? item.column() : item.property();
|
String property = isMap ? item.column() : item.property();
|
||||||
if (isMap ? filedName.equalsIgnoreCase(property) : filedName.equals(property)) {
|
if (isMap ? filedName.equalsIgnoreCase(property) : filedName.equals(property)) {
|
||||||
mapping = item;
|
mapping = item;
|
||||||
|
@ -660,7 +663,6 @@ public class ResultMapper {
|
||||||
Id id = annotation.id();
|
Id id = annotation.id();
|
||||||
String column = annotation.column();
|
String column = annotation.column();
|
||||||
String columnValue = rs.getString(column);
|
String columnValue = rs.getString(column);
|
||||||
Util.getLogger().info(Arrays.toString(rs.getColumnName()));
|
|
||||||
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
if (Objects.isNull(columnValue) || "".equals(columnValue)) {
|
||||||
columnValue = rs.getString(column.toUpperCase());
|
columnValue = rs.getString(column.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,15 @@ import aiyh.utils.Util;
|
||||||
import aiyh.utils.mapUtil.UtilHashMap;
|
import aiyh.utils.mapUtil.UtilHashMap;
|
||||||
import aiyh.utils.mapUtil.UtilLinkedHashMap;
|
import aiyh.utils.mapUtil.UtilLinkedHashMap;
|
||||||
import aiyh.utils.sqlUtil.builderSql.BuilderSql;
|
import aiyh.utils.sqlUtil.builderSql.BuilderSql;
|
||||||
import aiyh.utils.sqlUtil.sqlResult.impl.PrepSqlResultImpl;
|
|
||||||
import aiyh.utils.sqlUtil.sqlResult.impl.BatchSqlResultImpl;
|
import aiyh.utils.sqlUtil.sqlResult.impl.BatchSqlResultImpl;
|
||||||
|
import aiyh.utils.sqlUtil.sqlResult.impl.PrepSqlResultImpl;
|
||||||
import aiyh.utils.sqlUtil.whereUtil.Where;
|
import aiyh.utils.sqlUtil.whereUtil.Where;
|
||||||
import aiyh.utils.sqlUtil.whereUtil.impl.WhereImpl;
|
import aiyh.utils.sqlUtil.whereUtil.impl.WhereImpl;
|
||||||
import weaver.conn.RecordSet;
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
|
||||||
import java.beans.BeanInfo;
|
import java.beans.BeanInfo;
|
||||||
import java.beans.IntrospectionException;
|
|
||||||
import java.beans.Introspector;
|
import java.beans.Introspector;
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -29,15 +26,16 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
public class BuilderSqlImpl implements BuilderSql {
|
public class BuilderSqlImpl implements BuilderSql {
|
||||||
private String DB_TYPE;
|
private final String DB_TYPE;
|
||||||
|
|
||||||
{
|
{
|
||||||
// 获取当前数据库的类型
|
// 获取当前数据库的类型
|
||||||
this.DB_TYPE = (new RecordSet()).getDBType();
|
this.DB_TYPE = (new RecordSet()).getDBType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建插入语句
|
* 构建插入语句
|
||||||
|
*
|
||||||
* @param tableName 数据库表名
|
* @param tableName 数据库表名
|
||||||
* @param mapConfig 数据库字段和值
|
* @param mapConfig 数据库字段和值
|
||||||
* @return 自定义SQL实体类
|
* @return 自定义SQL实体类
|
||||||
|
@ -63,21 +61,22 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(" )");
|
sqlBuilder.append(" )");
|
||||||
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过实体类构建插入SQL
|
* 通过实体类构建插入SQL
|
||||||
* @param tableName 数据库表名
|
*
|
||||||
* @param t 实体类对象
|
* @param tableName 数据库表名
|
||||||
* @param <T> 实体类对象泛型
|
* @param t 实体类对象
|
||||||
* @return SQL结果对象
|
* @param <T> 实体类对象泛型
|
||||||
*/
|
* @return SQL结果对象
|
||||||
public <T> PrepSqlResultImpl insertSqlByEntity(String tableName, T t){
|
*/
|
||||||
|
public <T> PrepSqlResultImpl insertSqlByEntity(String tableName, T t) {
|
||||||
List<Object> args = new ArrayList<>();
|
List<Object> args = new ArrayList<>();
|
||||||
StringBuilder sqlBuilder = new StringBuilder("insert into ");
|
StringBuilder sqlBuilder = new StringBuilder("insert into ");
|
||||||
sqlBuilder.append(tableName);
|
sqlBuilder.append(tableName);
|
||||||
StringBuilder fieldBuilder = new StringBuilder();
|
StringBuilder fieldBuilder = new StringBuilder();
|
||||||
StringBuilder valueBuilder = new StringBuilder();
|
StringBuilder valueBuilder = new StringBuilder();
|
||||||
|
|
||||||
BeanInfo beanInfo = null;
|
BeanInfo beanInfo = null;
|
||||||
try {
|
try {
|
||||||
beanInfo = Introspector.getBeanInfo(t.getClass(), Object.class);
|
beanInfo = Introspector.getBeanInfo(t.getClass(), Object.class);
|
||||||
|
@ -88,7 +87,7 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
Object invoke = readMethod.invoke(t);
|
Object invoke = readMethod.invoke(t);
|
||||||
// System.out.println(name);
|
// System.out.println(name);
|
||||||
// System.out.println(invoke);
|
// System.out.println(invoke);
|
||||||
if(invoke == null){
|
if (invoke == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fieldBuilder.append(name);
|
fieldBuilder.append(name);
|
||||||
|
@ -106,10 +105,11 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(" )");
|
sqlBuilder.append(" )");
|
||||||
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建批量插入SQL
|
* 构建批量插入SQL
|
||||||
* @param tableName 表名
|
*
|
||||||
|
* @param tableName 表名
|
||||||
* @param mapListConfig 表对应的字段和值映射list
|
* @param mapListConfig 表对应的字段和值映射list
|
||||||
* @return 自定义批量SQL实体类
|
* @return 自定义批量SQL实体类
|
||||||
*/
|
*/
|
||||||
|
@ -142,14 +142,15 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(" )");
|
sqlBuilder.append(" )");
|
||||||
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建批量插入SQL
|
* 构建批量插入SQL
|
||||||
* @param tableName 数据库表名
|
*
|
||||||
* @param list 实体类数组
|
* @param tableName 数据库表名
|
||||||
* @param <T> 实体类泛型
|
* @param list 实体类数组
|
||||||
* @return SQL对象
|
* @param <T> 实体类泛型
|
||||||
*/
|
* @return SQL对象
|
||||||
|
*/
|
||||||
public <T> BatchSqlResultImpl insertBatchSqlByEntity(String tableName, List<T> list) {
|
public <T> BatchSqlResultImpl insertBatchSqlByEntity(String tableName, List<T> list) {
|
||||||
StringBuilder sqlBuilder = new StringBuilder("insert into ");
|
StringBuilder sqlBuilder = new StringBuilder("insert into ");
|
||||||
sqlBuilder.append(tableName);
|
sqlBuilder.append(tableName);
|
||||||
|
@ -157,11 +158,11 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
StringBuilder valueBuilder = new StringBuilder();
|
StringBuilder valueBuilder = new StringBuilder();
|
||||||
List<List> args = new ArrayList<>();
|
List<List> args = new ArrayList<>();
|
||||||
AtomicInteger i = new AtomicInteger();
|
AtomicInteger i = new AtomicInteger();
|
||||||
|
|
||||||
list.forEach(item -> {
|
list.forEach(item -> {
|
||||||
List<Object> arg = new ArrayList<>();
|
List<Object> arg = new ArrayList<>();
|
||||||
BeanInfo beanInfo = null;
|
BeanInfo beanInfo = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
beanInfo = Introspector.getBeanInfo(item.getClass(), Object.class);
|
beanInfo = Introspector.getBeanInfo(item.getClass(), Object.class);
|
||||||
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
||||||
|
@ -169,7 +170,7 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
String name = proper.getName();
|
String name = proper.getName();
|
||||||
Method readMethod = proper.getReadMethod();
|
Method readMethod = proper.getReadMethod();
|
||||||
Object invoke = readMethod.invoke(item);
|
Object invoke = readMethod.invoke(item);
|
||||||
if(invoke == null){
|
if (invoke == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i.get() == 0) {
|
if (i.get() == 0) {
|
||||||
|
@ -192,12 +193,13 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(" )");
|
sqlBuilder.append(" )");
|
||||||
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建更新语句
|
* 构建更新语句
|
||||||
|
*
|
||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @param mapConfig 表名所对应键值对
|
* @param mapConfig 表名所对应键值对
|
||||||
* @param where 更新数据的条件
|
* @param where 更新数据的条件
|
||||||
* @return 自定义SQL实体类
|
* @return 自定义SQL实体类
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -222,16 +224,17 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
}
|
}
|
||||||
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建更新SQL语句
|
* 构建更新SQL语句
|
||||||
* @param tableName 数据库表名
|
*
|
||||||
* @param t 实体类对象
|
* @param tableName 数据库表名
|
||||||
* @param where 更新条件对象
|
* @param t 实体类对象
|
||||||
* @param <T> 实体类泛型
|
* @param where 更新条件对象
|
||||||
* @return 构建后的SQL对象
|
* @param <T> 实体类泛型
|
||||||
*/
|
* @return 构建后的SQL对象
|
||||||
public <T> PrepSqlResultImpl updateSqlByEntity(String tableName, T t, Where where){
|
*/
|
||||||
|
public <T> PrepSqlResultImpl updateSqlByEntity(String tableName, T t, Where where) {
|
||||||
this.verifyWhere(where);
|
this.verifyWhere(where);
|
||||||
StringBuilder sqlBuilder = new StringBuilder("update ");
|
StringBuilder sqlBuilder = new StringBuilder("update ");
|
||||||
StringBuilder fieldValue = new StringBuilder();
|
StringBuilder fieldValue = new StringBuilder();
|
||||||
|
@ -246,7 +249,7 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
String name = proper.getName();
|
String name = proper.getName();
|
||||||
Method readMethod = proper.getReadMethod();
|
Method readMethod = proper.getReadMethod();
|
||||||
Object invoke = readMethod.invoke(t);
|
Object invoke = readMethod.invoke(t);
|
||||||
if(invoke == null){
|
if (invoke == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fieldValue.append(name);
|
fieldValue.append(name);
|
||||||
|
@ -264,12 +267,12 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
}
|
}
|
||||||
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
return new PrepSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @param mapListConfig 表名所对应的键值对数组
|
* @param mapListConfig 表名所对应的键值对数组
|
||||||
* @param whereList 条件数组
|
* @param whereList 条件数组
|
||||||
* @return 批量更新只适用于更新条件一样,但是参数不同的时候,比如更新条件都是id= ?,但是各id的数值不一样
|
* @return 批量更新只适用于更新条件一样,但是参数不同的时候,比如更新条件都是id= ?,但是各id的数值不一样
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -305,15 +308,16 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(whereList.get(0).getSql());
|
sqlBuilder.append(whereList.get(0).getSql());
|
||||||
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过实体类构建批量更新SQL
|
* 通过实体类构建批量更新SQL
|
||||||
* @param tableName 数据库表名
|
*
|
||||||
* @param list 实体类集合
|
* @param tableName 数据库表名
|
||||||
* @param whereList 更新条件集合,一一对应更新数据集合
|
* @param list 实体类集合
|
||||||
* @param <T> 泛型
|
* @param whereList 更新条件集合,一一对应更新数据集合
|
||||||
* @return 构建后的批量SQL对象
|
* @param <T> 泛型
|
||||||
*/
|
* @return 构建后的批量SQL对象
|
||||||
|
*/
|
||||||
public <T> BatchSqlResultImpl updateBatchSqlByEntity(String tableName, List<T> list, List<Where> whereList) {
|
public <T> BatchSqlResultImpl updateBatchSqlByEntity(String tableName, List<T> list, List<Where> whereList) {
|
||||||
this.verifyWhereList(whereList);
|
this.verifyWhereList(whereList);
|
||||||
if (list.size() != whereList.size()) {
|
if (list.size() != whereList.size()) {
|
||||||
|
@ -335,7 +339,7 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
String name = proper.getName();
|
String name = proper.getName();
|
||||||
Method readMethod = proper.getReadMethod();
|
Method readMethod = proper.getReadMethod();
|
||||||
Object invoke = readMethod.invoke(item);
|
Object invoke = readMethod.invoke(item);
|
||||||
if(invoke == null){
|
if (invoke == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i.get() == 0) {
|
if (i.get() == 0) {
|
||||||
|
@ -358,9 +362,10 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(whereList.get(0).getSql());
|
sqlBuilder.append(whereList.get(0).getSql());
|
||||||
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
return new BatchSqlResultImpl(sqlBuilder.toString(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建插入SQL语句
|
* 构建插入SQL语句
|
||||||
|
*
|
||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @param mapConfig 参数
|
* @param mapConfig 参数
|
||||||
* @return 拼接好的SQL
|
* @return 拼接好的SQL
|
||||||
|
@ -387,9 +392,10 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(" )");
|
sqlBuilder.append(" )");
|
||||||
return sqlBuilder.toString();
|
return sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建更新SQL语句
|
* 构建更新SQL语句
|
||||||
|
*
|
||||||
* @param tableName 表名
|
* @param tableName 表名
|
||||||
* @param mapConfig 参数
|
* @param mapConfig 参数
|
||||||
* @return 拼接好的SQL
|
* @return 拼接好的SQL
|
||||||
|
@ -415,27 +421,27 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
sqlBuilder.append(where.getSql());
|
sqlBuilder.append(where.getSql());
|
||||||
return sqlBuilder.toString();
|
return sqlBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证构建SQL参数的,并且过滤到为null的数据
|
* 验证构建SQL参数的,并且过滤到为null的数据
|
||||||
|
*
|
||||||
* @param map 验证SQL参数map对象
|
* @param map 验证SQL参数map对象
|
||||||
* @return 验证后的UtilHashMap
|
* @return 验证后的UtilHashMap
|
||||||
*/
|
*/
|
||||||
public UtilHashMap<String, Object> verifyMap(Map<String, Object> map) {
|
public UtilHashMap<String, Object> verifyMap(Map<String, Object> map) {
|
||||||
UtilHashMap<String, Object> filterMap = Util.createUtilHashMap()
|
UtilHashMap<String, Object> filterMap = Util.createUtilHashMap()
|
||||||
.uPutAll(map)
|
.uPutAll(map)
|
||||||
.filter((key, value) -> !Objects.isNull(key) && !Objects.isNull(value));
|
.filter((key, value) -> !Objects.isNull(key) && !Objects.isNull(value));
|
||||||
if (Util.mapIsNullOrEmpty(filterMap)) {
|
if (Util.mapIsNullOrEmpty(filterMap)) {
|
||||||
throw new RuntimeException("map为空或没有数据! map is null or empty!");
|
throw new RuntimeException("map为空或没有数据! map is null or empty!");
|
||||||
}
|
}
|
||||||
return filterMap;
|
return filterMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证批量SQL构建的数据
|
* 验证批量SQL构建的数据
|
||||||
|
*
|
||||||
* @param mapList 批量构建SQL的的工具
|
* @param mapList 批量构建SQL的的工具
|
||||||
* @return 验证和过滤后的数据
|
* @return 验证和过滤后的数据
|
||||||
*/
|
*/
|
||||||
|
@ -444,18 +450,19 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
throw new RuntimeException("mapList为null!mapList is null!");
|
throw new RuntimeException("mapList为null!mapList is null!");
|
||||||
}
|
}
|
||||||
List<UtilLinkedHashMap<String, Object>> collect = mapList.stream().map(item ->
|
List<UtilLinkedHashMap<String, Object>> collect = mapList.stream().map(item ->
|
||||||
Util.createUtilLinkedHashMap()
|
Util.createUtilLinkedHashMap()
|
||||||
.uPutAll(item)
|
.uPutAll(item)
|
||||||
.filter((key, value) -> !Objects.isNull(key) && !Objects.isNull(value))
|
.filter((key, value) -> !Objects.isNull(key) && !Objects.isNull(value))
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
if (mapList.size() == 0) {
|
if (mapList.size() == 0) {
|
||||||
throw new RuntimeException("mapList没有数据!mapList is empty!");
|
throw new RuntimeException("mapList没有数据!mapList is empty!");
|
||||||
}
|
}
|
||||||
return collect;
|
return collect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证where条件是否符合
|
* 验证where条件是否符合
|
||||||
|
*
|
||||||
* @param where where 条件对象
|
* @param where where 条件对象
|
||||||
*/
|
*/
|
||||||
private void verifyWhere(Where where) {
|
private void verifyWhere(Where where) {
|
||||||
|
@ -463,16 +470,18 @@ public class BuilderSqlImpl implements BuilderSql {
|
||||||
throw new RuntimeException("where为null! where is null!");
|
throw new RuntimeException("where为null! where is null!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证where条件集合是否符合
|
* 验证where条件集合是否符合
|
||||||
|
*
|
||||||
* @param whereList where 条件对象集合
|
* @param whereList where 条件对象集合
|
||||||
*/
|
*/
|
||||||
private void verifyWhereList(List<Where> whereList) {
|
private void verifyWhereList(List<Where> whereList) {
|
||||||
if (whereList == null) {
|
if (whereList == null) {
|
||||||
throw new RuntimeException("whereList为null! whereList is null!");
|
throw new RuntimeException("whereList为null! whereList is null!");
|
||||||
}
|
}
|
||||||
whereList.forEach(item->{
|
whereList.forEach(item -> {
|
||||||
if(item == null){
|
if (item == null) {
|
||||||
throw new RuntimeException("whereList中数据为null! whereDate is null in whereList!");
|
throw new RuntimeException("whereList中数据为null! whereDate is null in whereList!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常用Content-Type类型枚举
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.0.11
|
||||||
|
*/
|
||||||
|
public enum ContentType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准表单编码,当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2…)
|
||||||
|
*/
|
||||||
|
FORM_URLENCODED("application/x-www-form-urlencoded"),
|
||||||
|
/**
|
||||||
|
* 文件上传编码,浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition,并加上分割符(boundary)
|
||||||
|
*/
|
||||||
|
MULTIPART("multipart/form-data"),
|
||||||
|
/**
|
||||||
|
* Rest请求JSON编码
|
||||||
|
*/
|
||||||
|
JSON("application/json"),
|
||||||
|
/**
|
||||||
|
* Rest请求XML编码
|
||||||
|
*/
|
||||||
|
XML("application/xml"),
|
||||||
|
/**
|
||||||
|
* text/plain编码
|
||||||
|
*/
|
||||||
|
TEXT_PLAIN("text/plain"),
|
||||||
|
/**
|
||||||
|
* Rest请求text/xml编码
|
||||||
|
*/
|
||||||
|
TEXT_XML("text/xml"),
|
||||||
|
/**
|
||||||
|
* text/html编码
|
||||||
|
*/
|
||||||
|
TEXT_HTML("text/html"),
|
||||||
|
/**
|
||||||
|
* application/octet-stream编码
|
||||||
|
*/
|
||||||
|
OCTET_STREAM("application/octet-stream");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param value ContentType值
|
||||||
|
*/
|
||||||
|
ContentType(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取value值
|
||||||
|
*
|
||||||
|
* @return value值
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出Content-Type字符串,附带编码信息
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return Content-Type字符串
|
||||||
|
*/
|
||||||
|
public String toString(Charset charset) {
|
||||||
|
return build(this.value, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为默认Content-Type,默认包括{@code null}和application/x-www-form-urlencoded
|
||||||
|
*
|
||||||
|
* @param contentType 内容类型
|
||||||
|
* @return 是否为默认Content-Type
|
||||||
|
* @since 4.1.5
|
||||||
|
*/
|
||||||
|
public static boolean isDefault(String contentType) {
|
||||||
|
return null == contentType || isFormUrlEncode(contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为application/x-www-form-urlencoded
|
||||||
|
*
|
||||||
|
* @param contentType 内容类型
|
||||||
|
* @return 是否为application/x-www-form-urlencoded
|
||||||
|
*/
|
||||||
|
public static boolean isFormUrlEncode(String contentType) {
|
||||||
|
return StrUtil.startWithIgnoreCase(contentType, FORM_URLENCODED.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求参数的body中判断请求的Content-Type类型,支持的类型有:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. application/json
|
||||||
|
* 1. application/xml
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param body 请求参数体
|
||||||
|
* @return Content-Type类型,如果无法判断返回null
|
||||||
|
*/
|
||||||
|
public static ContentType get(String body) {
|
||||||
|
ContentType contentType = null;
|
||||||
|
if (StrUtil.isNotBlank(body)) {
|
||||||
|
char firstChar = body.charAt(0);
|
||||||
|
switch (firstChar) {
|
||||||
|
case '{':
|
||||||
|
case '[':
|
||||||
|
// JSON请求体
|
||||||
|
contentType = JSON;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
// XML请求体
|
||||||
|
contentType = XML;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出Content-Type字符串,附带编码信息
|
||||||
|
*
|
||||||
|
* @param contentType Content-Type类型
|
||||||
|
* @param charset 编码
|
||||||
|
* @return Content-Type字符串
|
||||||
|
* @since 4.5.4
|
||||||
|
*/
|
||||||
|
public static String build(String contentType, Charset charset) {
|
||||||
|
return StrUtil.format("{};charset={}", contentType, charset.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出Content-Type字符串,附带编码信息
|
||||||
|
*
|
||||||
|
* @param contentType Content-Type 枚举类型
|
||||||
|
* @param charset 编码
|
||||||
|
* @return Content-Type字符串
|
||||||
|
* @since 5.7.15
|
||||||
|
*/
|
||||||
|
public static String build(ContentType contentType, Charset charset) {
|
||||||
|
return build(contentType.getValue(), charset);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,229 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局头部信息<br>
|
||||||
|
* 所有Http请求将共用此全局头部信息,除非在{@link HttpRequest}中自定义头部信息覆盖之
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public enum GlobalHeaders {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储头信息
|
||||||
|
*/
|
||||||
|
final Map<String, List<String>> headers = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
GlobalHeaders() {
|
||||||
|
putDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入默认的头部信息
|
||||||
|
*
|
||||||
|
* @param isReset 是否重置所有头部信息(删除自定义保留默认)
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders putDefault(boolean isReset) {
|
||||||
|
// 解决HttpURLConnection中无法自定义Host等头信息的问题
|
||||||
|
// https://stackoverflow.com/questions/9096987/how-to-overwrite-http-header-host-in-a-httpurlconnection/9098440
|
||||||
|
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
|
||||||
|
|
||||||
|
// 解决server certificate change is restricted during renegotiation问题
|
||||||
|
System.setProperty("jdk.tls.allowUnsafeServerCertChange", "true");
|
||||||
|
System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
|
||||||
|
|
||||||
|
if (isReset) {
|
||||||
|
this.headers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
header(aiyh.utils.tool.cn.hutool.http.Header.ACCEPT, "text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", true);
|
||||||
|
header(aiyh.utils.tool.cn.hutool.http.Header.ACCEPT_ENCODING, "gzip, deflate", true);
|
||||||
|
header(aiyh.utils.tool.cn.hutool.http.Header.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8", true);
|
||||||
|
// 此Header只有在post请求中有用,因此在HttpRequest的method方法中设置此头信息,此处去掉
|
||||||
|
// header(Header.CONTENT_TYPE, ContentType.FORM_URLENCODED.toString(CharsetUtil.CHARSET_UTF_8), true);
|
||||||
|
header(aiyh.utils.tool.cn.hutool.http.Header.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool", true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Headers start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
public String header(String name) {
|
||||||
|
final List<String> values = headerList(name);
|
||||||
|
if (CollectionUtil.isEmpty(values)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return values.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息列表
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
public List<String> headerList(String name) {
|
||||||
|
if (StrUtil.isBlank(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers.get(name.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
public String header(aiyh.utils.tool.cn.hutool.http.Header name) {
|
||||||
|
if (null == name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return header(name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @param isOverride 是否覆盖已有值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalHeaders header(String name, String value, boolean isOverride) {
|
||||||
|
if (null != name && null != value) {
|
||||||
|
final List<String> values = headers.get(name.trim());
|
||||||
|
if (isOverride || CollectionUtil.isEmpty(values)) {
|
||||||
|
final ArrayList<String> valueList = new ArrayList<>();
|
||||||
|
valueList.add(value);
|
||||||
|
headers.put(name.trim(), valueList);
|
||||||
|
} else {
|
||||||
|
values.add(value.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @param isOverride 是否覆盖已有值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders header(aiyh.utils.tool.cn.hutool.http.Header name, String value, boolean isOverride) {
|
||||||
|
return header(name.toString(), value, isOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 覆盖模式,则替换之前的值
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders header(aiyh.utils.tool.cn.hutool.http.Header name, String value) {
|
||||||
|
return header(name.toString(), value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 覆盖模式,则替换之前的值
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders header(String name, String value) {
|
||||||
|
return header(name, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头<br>
|
||||||
|
* 不覆盖原有请求头
|
||||||
|
*
|
||||||
|
* @param headers 请求头
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders header(Map<String, List<String>> headers) {
|
||||||
|
if (MapUtil.isEmpty(headers)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name;
|
||||||
|
for (Entry<String, List<String>> entry : headers.entrySet()) {
|
||||||
|
name = entry.getKey();
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
this.header(name, StrUtil.nullToEmpty(value), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalHeaders removeHeader(String name) {
|
||||||
|
if (name != null) {
|
||||||
|
headers.remove(name.trim());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalHeaders removeHeader(Header name) {
|
||||||
|
return removeHeader(name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取headers
|
||||||
|
*
|
||||||
|
* @return Headers Map
|
||||||
|
*/
|
||||||
|
public Map<String, List<String>> headers() {
|
||||||
|
return Collections.unmodifiableMap(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有头信息,包括全局头信息
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.13
|
||||||
|
*/
|
||||||
|
synchronized public GlobalHeaders clearHeaders() {
|
||||||
|
this.headers.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------- Headers end
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局的拦截器<br>
|
||||||
|
* 包括请求拦截器和响应拦截器
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public enum GlobalInterceptor {
|
||||||
|
INSTANCE;
|
||||||
|
|
||||||
|
private final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpRequest> requestInterceptors = new HttpInterceptor.Chain<>();
|
||||||
|
private final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpResponse> responseInterceptors = new HttpInterceptor.Chain<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置拦截器,用于在请求前重新编辑请求
|
||||||
|
*
|
||||||
|
* @param interceptor 拦截器实现
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalInterceptor addRequestInterceptor(HttpInterceptor<aiyh.utils.tool.cn.hutool.http.HttpRequest> interceptor) {
|
||||||
|
this.requestInterceptors.addChain(interceptor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置拦截器,用于在响应读取后完成编辑或读取
|
||||||
|
*
|
||||||
|
* @param interceptor 拦截器实现
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalInterceptor addResponseInterceptor(HttpInterceptor<aiyh.utils.tool.cn.hutool.http.HttpResponse> interceptor) {
|
||||||
|
this.responseInterceptors.addChain(interceptor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空请求和响应拦截器
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public GlobalInterceptor clear() {
|
||||||
|
clearRequest();
|
||||||
|
clearResponse();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空请求拦截器
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalInterceptor clearRequest() {
|
||||||
|
requestInterceptors.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空响应拦截器
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
synchronized public GlobalInterceptor clearResponse() {
|
||||||
|
responseInterceptors.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制请求过滤器列表
|
||||||
|
*
|
||||||
|
* @return {@link HttpInterceptor.Chain}
|
||||||
|
*/
|
||||||
|
HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpRequest> getCopiedRequestInterceptor() {
|
||||||
|
final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpRequest> copied = new HttpInterceptor.Chain<>();
|
||||||
|
for (HttpInterceptor<HttpRequest> interceptor : this.requestInterceptors) {
|
||||||
|
copied.addChain(interceptor);
|
||||||
|
}
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制响应过滤器列表
|
||||||
|
*
|
||||||
|
* @return {@link HttpInterceptor.Chain}
|
||||||
|
*/
|
||||||
|
HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpResponse> getCopiedResponseInterceptor() {
|
||||||
|
final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpResponse> copied = new HttpInterceptor.Chain<>();
|
||||||
|
for (HttpInterceptor<HttpResponse> interceptor : this.responseInterceptors) {
|
||||||
|
copied.addChain(interceptor);
|
||||||
|
}
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,539 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Console;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.SafeConcurrentHashMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML过滤器,用于去除XSS(Cross Site Scripting) 漏洞隐患。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 此类中的方法非线程安全
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* String clean = new HTMLFilter().filter(input);
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* 此类来自:http://xss-html-filter.sf.net
|
||||||
|
*
|
||||||
|
* @author Joseph O'Connell
|
||||||
|
* @author Cal Hendersen
|
||||||
|
* @author Michael Semb Wever
|
||||||
|
*/
|
||||||
|
public final class HTMLFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* regex flag union representing /si modifiers in php
|
||||||
|
**/
|
||||||
|
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||||
|
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
|
||||||
|
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
|
||||||
|
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||||
|
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||||
|
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||||
|
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_AMP = Pattern.compile("&");
|
||||||
|
private static final Pattern P_QUOTE = Pattern.compile("\"");
|
||||||
|
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||||
|
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||||
|
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||||
|
|
||||||
|
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||||
|
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new SafeConcurrentHashMap<>();
|
||||||
|
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new SafeConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set of allowed html elements, along with allowed attributes for each element
|
||||||
|
**/
|
||||||
|
private final Map<String, List<String>> vAllowed;
|
||||||
|
/**
|
||||||
|
* counts of open tags for each (allowable) html element
|
||||||
|
**/
|
||||||
|
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* html elements which must always be self-closing (e.g. "<img />")
|
||||||
|
**/
|
||||||
|
private final String[] vSelfClosingTags;
|
||||||
|
/**
|
||||||
|
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||||
|
**/
|
||||||
|
private final String[] vNeedClosingTags;
|
||||||
|
/**
|
||||||
|
* set of disallowed html elements
|
||||||
|
**/
|
||||||
|
private final String[] vDisallowed;
|
||||||
|
/**
|
||||||
|
* attributes which should be checked for valid protocols
|
||||||
|
**/
|
||||||
|
private final String[] vProtocolAtts;
|
||||||
|
/**
|
||||||
|
* allowed protocols
|
||||||
|
**/
|
||||||
|
private final String[] vAllowedProtocols;
|
||||||
|
/**
|
||||||
|
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
|
||||||
|
**/
|
||||||
|
private final String[] vRemoveBlanks;
|
||||||
|
/**
|
||||||
|
* entities allowed within html markup
|
||||||
|
**/
|
||||||
|
private final String[] vAllowedEntities;
|
||||||
|
/**
|
||||||
|
* flag determining whether comments are allowed in input String.
|
||||||
|
*/
|
||||||
|
private final boolean stripComment;
|
||||||
|
private final boolean encodeQuotes;
|
||||||
|
private boolean vDebug = false;
|
||||||
|
/**
|
||||||
|
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>" becomes "<b> text </g>").
|
||||||
|
* If set to false, unbalanced angle brackets will be
|
||||||
|
* html escaped.
|
||||||
|
*/
|
||||||
|
private final boolean alwaysMakeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
public HTMLFilter() {
|
||||||
|
vAllowed = new HashMap<>();
|
||||||
|
|
||||||
|
final ArrayList<String> a_atts = new ArrayList<>();
|
||||||
|
a_atts.add("href");
|
||||||
|
a_atts.add("target");
|
||||||
|
vAllowed.put("a", a_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> img_atts = new ArrayList<>();
|
||||||
|
img_atts.add("src");
|
||||||
|
img_atts.add("width");
|
||||||
|
img_atts.add("height");
|
||||||
|
img_atts.add("alt");
|
||||||
|
vAllowed.put("img", img_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> no_atts = new ArrayList<>();
|
||||||
|
vAllowed.put("b", no_atts);
|
||||||
|
vAllowed.put("strong", no_atts);
|
||||||
|
vAllowed.put("i", no_atts);
|
||||||
|
vAllowed.put("em", no_atts);
|
||||||
|
|
||||||
|
vSelfClosingTags = new String[]{"img"};
|
||||||
|
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vDisallowed = new String[]{};
|
||||||
|
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
|
||||||
|
vProtocolAtts = new String[]{"src", "href"};
|
||||||
|
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
|
||||||
|
stripComment = true;
|
||||||
|
encodeQuotes = true;
|
||||||
|
alwaysMakeTags = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set debug flag to true. Otherwise use default settings. See the default constructor.
|
||||||
|
*
|
||||||
|
* @param debug turn debug on with a true argument
|
||||||
|
*/
|
||||||
|
public HTMLFilter(final boolean debug) {
|
||||||
|
this();
|
||||||
|
vDebug = debug;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map-parameter configurable constructor.
|
||||||
|
*
|
||||||
|
* @param conf map containing configuration. keys match field names.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public HTMLFilter(final Map<String, Object> conf) {
|
||||||
|
|
||||||
|
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||||
|
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||||
|
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||||
|
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||||
|
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||||
|
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||||
|
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||||
|
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||||
|
|
||||||
|
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||||
|
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||||
|
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||||
|
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||||
|
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||||
|
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||||
|
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||||
|
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||||
|
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||||
|
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||||
|
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
vTagCounts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debug(final String msg) {
|
||||||
|
if (vDebug) {
|
||||||
|
Console.log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// my versions of some PHP library functions
|
||||||
|
public static String chr(final int decimal) {
|
||||||
|
return String.valueOf((char) decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String htmlSpecialChars(final String s) {
|
||||||
|
String result = s;
|
||||||
|
result = regexReplace(P_AMP, "&", result);
|
||||||
|
result = regexReplace(P_QUOTE, """, result);
|
||||||
|
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||||
|
result = regexReplace(P_RIGHT_ARROW, ">", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* given a user submitted input String, filter out any invalid or restricted html.
|
||||||
|
*
|
||||||
|
* @param input text (i.e. submitted by a user) than may contain html
|
||||||
|
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||||
|
*/
|
||||||
|
public String filter(final String input) {
|
||||||
|
reset();
|
||||||
|
String s = input;
|
||||||
|
|
||||||
|
debug("************************************************");
|
||||||
|
debug(" INPUT: " + input);
|
||||||
|
|
||||||
|
s = escapeComments(s);
|
||||||
|
debug(" escapeComments: " + s);
|
||||||
|
|
||||||
|
s = balanceHTML(s);
|
||||||
|
debug(" balanceHTML: " + s);
|
||||||
|
|
||||||
|
s = checkTags(s);
|
||||||
|
debug(" checkTags: " + s);
|
||||||
|
|
||||||
|
s = processRemoveBlanks(s);
|
||||||
|
debug("processRemoveBlanks: " + s);
|
||||||
|
|
||||||
|
s = validateEntities(s);
|
||||||
|
debug(" validateEntites: " + s);
|
||||||
|
|
||||||
|
debug("************************************************\n\n");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlwaysMakeTags() {
|
||||||
|
return alwaysMakeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStripComments() {
|
||||||
|
return stripComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escapeComments(final String s) {
|
||||||
|
final Matcher m = P_COMMENTS.matcher(s);
|
||||||
|
final StringBuffer buf = new StringBuffer();
|
||||||
|
if (m.find()) {
|
||||||
|
final String match = m.group(1); // (.*?)
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String balanceHTML(String s) {
|
||||||
|
if (alwaysMakeTags) {
|
||||||
|
//
|
||||||
|
// try and form html
|
||||||
|
//
|
||||||
|
s = regexReplace(P_END_ARROW, "", s);
|
||||||
|
s = regexReplace(P_BODY_TO_END, "<$1>", s);
|
||||||
|
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// escape stray brackets
|
||||||
|
//
|
||||||
|
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||||
|
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||||
|
|
||||||
|
//
|
||||||
|
// the last regexp causes '<>' entities to appear
|
||||||
|
// (we need to do a lookahead assertion so that the last bracket can
|
||||||
|
// be used in the next pass of the regexp)
|
||||||
|
//
|
||||||
|
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkTags(String s) {
|
||||||
|
Matcher m = P_TAGS.matcher(s);
|
||||||
|
|
||||||
|
final StringBuffer buf = new StringBuffer();
|
||||||
|
while (m.find()) {
|
||||||
|
String replaceStr = m.group(1);
|
||||||
|
replaceStr = processTag(replaceStr);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
// these get tallied in processTag
|
||||||
|
// (remember to reset before subsequent calls to filter method)
|
||||||
|
final StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||||
|
for (String key : vTagCounts.keySet()) {
|
||||||
|
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
|
||||||
|
sBuilder.append("</").append(key).append(">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = sBuilder.toString();
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processRemoveBlanks(final String s) {
|
||||||
|
String result = s;
|
||||||
|
for (String tag : vRemoveBlanks) {
|
||||||
|
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
|
||||||
|
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
|
||||||
|
}
|
||||||
|
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
|
||||||
|
if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
|
||||||
|
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
|
||||||
|
}
|
||||||
|
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
|
||||||
|
Matcher m = regex_pattern.matcher(s);
|
||||||
|
return m.replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processTag(final String s) {
|
||||||
|
// ending tags
|
||||||
|
Matcher m = P_END_TAG.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String name = m.group(1).toLowerCase();
|
||||||
|
if (allowed(name)) {
|
||||||
|
if (!inArray(name, vSelfClosingTags)) {
|
||||||
|
if (vTagCounts.containsKey(name)) {
|
||||||
|
vTagCounts.put(name, vTagCounts.get(name) - 1);
|
||||||
|
return "</" + name + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// starting tags
|
||||||
|
m = P_START_TAG.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String name = m.group(1).toLowerCase();
|
||||||
|
final String body = m.group(2);
|
||||||
|
String ending = m.group(3);
|
||||||
|
|
||||||
|
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
||||||
|
if (allowed(name)) {
|
||||||
|
final StringBuilder params = new StringBuilder();
|
||||||
|
|
||||||
|
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
|
||||||
|
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
|
||||||
|
final List<String> paramNames = new ArrayList<>();
|
||||||
|
final List<String> paramValues = new ArrayList<>();
|
||||||
|
while (m2.find()) {
|
||||||
|
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
||||||
|
paramValues.add(m2.group(3)); // (.*?)
|
||||||
|
}
|
||||||
|
while (m3.find()) {
|
||||||
|
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
||||||
|
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
||||||
|
}
|
||||||
|
|
||||||
|
String paramName, paramValue;
|
||||||
|
for (int ii = 0; ii < paramNames.size(); ii++) {
|
||||||
|
paramName = paramNames.get(ii).toLowerCase();
|
||||||
|
paramValue = paramValues.get(ii);
|
||||||
|
|
||||||
|
// debug( "paramName='" + paramName + "'" );
|
||||||
|
// debug( "paramValue='" + paramValue + "'" );
|
||||||
|
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||||
|
|
||||||
|
if (allowedAttribute(name, paramName)) {
|
||||||
|
if (inArray(paramName, vProtocolAtts)) {
|
||||||
|
paramValue = processParamProtocol(paramValue);
|
||||||
|
}
|
||||||
|
params.append(CharUtil.SPACE).append(paramName).append("=\"").append(paramValue).append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vSelfClosingTags)) {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vNeedClosingTags)) {
|
||||||
|
ending = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ending == null || ending.length() < 1) {
|
||||||
|
if (vTagCounts.containsKey(name)) {
|
||||||
|
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||||
|
} else {
|
||||||
|
vTagCounts.put(name, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
return "<" + name + params + ending + ">";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// comments
|
||||||
|
m = P_COMMENT.matcher(s);
|
||||||
|
if (!stripComment && m.find()) {
|
||||||
|
return "<" + m.group() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processParamProtocol(String s) {
|
||||||
|
s = decodeEntities(s);
|
||||||
|
final Matcher m = P_PROTOCOL.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String protocol = m.group(1);
|
||||||
|
if (!inArray(protocol, vAllowedProtocols)) {
|
||||||
|
// bad protocol, turn into local anchor link instead
|
||||||
|
s = "#" + s.substring(protocol.length() + 1);
|
||||||
|
if (s.startsWith("#//")) {
|
||||||
|
s = "#" + s.substring(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decodeEntities(String s) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
Matcher m = P_ENTITY.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.decode(match);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
buf = new StringBuffer();
|
||||||
|
m = P_ENTITY_UNICODE.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.parseInt(match, 16);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
buf = new StringBuffer();
|
||||||
|
m = P_ENCODE.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.parseInt(match, 16);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
s = validateEntities(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String validateEntities(final String s) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
// validate entities throughout the string
|
||||||
|
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String one = m.group(1); // ([^&;]*)
|
||||||
|
final String two = m.group(2); // (?=(;|&|$))
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
return encodeQuotes(buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encodeQuotes(final String s) {
|
||||||
|
if (encodeQuotes) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
Matcher m = P_VALID_QUOTES.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String one = m.group(1); // (>|^)
|
||||||
|
final String two = m.group(2); // ([^<]+?)
|
||||||
|
final String three = m.group(3); // (<|$)
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
return buf.toString();
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkEntity(final String preamble, final String term) {
|
||||||
|
|
||||||
|
return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidEntity(final String entity) {
|
||||||
|
return inArray(entity, vAllowedEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean inArray(final String s, final String[] array) {
|
||||||
|
for (String item : array) {
|
||||||
|
if (item != null && item.equals(s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allowed(final String name) {
|
||||||
|
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allowedAttribute(final String name, final String paramName) {
|
||||||
|
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http 头域
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public enum Header {
|
||||||
|
|
||||||
|
//------------------------------------------------------------- 通用头域
|
||||||
|
/**
|
||||||
|
* 提供验证头,例如:
|
||||||
|
* <pre>
|
||||||
|
* Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
AUTHORIZATION("Authorization"),
|
||||||
|
/**
|
||||||
|
* 提供给代理服务器的用于身份验证的凭证,例如:
|
||||||
|
* <pre>
|
||||||
|
* Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
PROXY_AUTHORIZATION("Proxy-Authorization"),
|
||||||
|
/**
|
||||||
|
* 提供日期和时间标志,说明报文是什么时间创建的
|
||||||
|
*/
|
||||||
|
DATE("Date"),
|
||||||
|
/**
|
||||||
|
* 允许客户端和服务器指定与请求/响应连接有关的选项
|
||||||
|
*/
|
||||||
|
CONNECTION("Connection"),
|
||||||
|
/**
|
||||||
|
* 给出发送端使用的MIME版本
|
||||||
|
*/
|
||||||
|
MIME_VERSION("MIME-Version"),
|
||||||
|
/**
|
||||||
|
* 如果报文采用了分块传输编码(chunked transfer encoding) 方式,就可以用这个首部列出位于报文拖挂(trailer)部分的首部集合
|
||||||
|
*/
|
||||||
|
TRAILER("Trailer"),
|
||||||
|
/**
|
||||||
|
* 告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式
|
||||||
|
*/
|
||||||
|
TRANSFER_ENCODING("Transfer-Encoding"),
|
||||||
|
/**
|
||||||
|
* 给出了发送端可能想要"升级"使用的新版本和协议
|
||||||
|
*/
|
||||||
|
UPGRADE("Upgrade"),
|
||||||
|
/**
|
||||||
|
* 显示了报文经过的中间节点
|
||||||
|
*/
|
||||||
|
VIA("Via"),
|
||||||
|
/**
|
||||||
|
* 指定请求和响应遵循的缓存机制
|
||||||
|
*/
|
||||||
|
CACHE_CONTROL("Cache-Control"),
|
||||||
|
/**
|
||||||
|
* 用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache- Control:no-cache相同
|
||||||
|
*/
|
||||||
|
PRAGMA("Pragma"),
|
||||||
|
/**
|
||||||
|
* 请求表示提交内容类型或返回返回内容的MIME类型
|
||||||
|
*/
|
||||||
|
CONTENT_TYPE("Content-Type"),
|
||||||
|
|
||||||
|
//------------------------------------------------------------- 请求头域
|
||||||
|
/**
|
||||||
|
* 指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回
|
||||||
|
*/
|
||||||
|
HOST("Host"),
|
||||||
|
/**
|
||||||
|
* 允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被 追踪。如果请求的uri没有自己的uri地址,Referer不能被发送。如果指定的是部分uri地址,则此地址应该是一个相对地址
|
||||||
|
*/
|
||||||
|
REFERER("Referer"),
|
||||||
|
/**
|
||||||
|
* 指定请求的域
|
||||||
|
*/
|
||||||
|
ORIGIN("Origin"),
|
||||||
|
/**
|
||||||
|
* HTTP客户端运行的浏览器类型的详细信息。通过该头部信息,web服务器可以判断到当前HTTP请求的客户端浏览器类别
|
||||||
|
*/
|
||||||
|
USER_AGENT("User-Agent"),
|
||||||
|
/**
|
||||||
|
* 指定客户端能够接收的内容类型,内容类型中的先后次序表示客户端接收的先后次序
|
||||||
|
*/
|
||||||
|
ACCEPT("Accept"),
|
||||||
|
/**
|
||||||
|
* 指定HTTP客户端浏览器用来展示返回信息所优先选择的语言
|
||||||
|
*/
|
||||||
|
ACCEPT_LANGUAGE("Accept-Language"),
|
||||||
|
/**
|
||||||
|
* 指定客户端浏览器可以支持的web服务器返回内容压缩编码类型
|
||||||
|
*/
|
||||||
|
ACCEPT_ENCODING("Accept-Encoding"),
|
||||||
|
/**
|
||||||
|
* 浏览器可以接受的字符编码集
|
||||||
|
*/
|
||||||
|
ACCEPT_CHARSET("Accept-Charset"),
|
||||||
|
/**
|
||||||
|
* HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器
|
||||||
|
*/
|
||||||
|
COOKIE("Cookie"),
|
||||||
|
/**
|
||||||
|
* 请求的内容长度
|
||||||
|
*/
|
||||||
|
CONTENT_LENGTH("Content-Length"),
|
||||||
|
|
||||||
|
//------------------------------------------------------------- 响应头域
|
||||||
|
/**
|
||||||
|
* 提供WWW验证响应头
|
||||||
|
*/
|
||||||
|
WWW_AUTHENTICATE("WWW-Authenticate"),
|
||||||
|
/**
|
||||||
|
* Cookie
|
||||||
|
*/
|
||||||
|
SET_COOKIE("Set-Cookie"),
|
||||||
|
/**
|
||||||
|
* Content-Encoding
|
||||||
|
*/
|
||||||
|
CONTENT_ENCODING("Content-Encoding"),
|
||||||
|
/**
|
||||||
|
* Content-Disposition
|
||||||
|
*/
|
||||||
|
CONTENT_DISPOSITION("Content-Disposition"),
|
||||||
|
/**
|
||||||
|
* ETag
|
||||||
|
*/
|
||||||
|
ETAG("ETag"),
|
||||||
|
/**
|
||||||
|
* 重定向指示到的URL
|
||||||
|
*/
|
||||||
|
LOCATION("Location");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
Header(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取值
|
||||||
|
*
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getValue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.EscapeUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML工具类
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 比如我们在使用爬虫爬取HTML页面后,需要对返回页面的HTML内容做一定处理,<br>
|
||||||
|
* 比如去掉指定标签(例如广告栏等)、去除JS、去掉样式等等,这些操作都可以使用此工具类完成。
|
||||||
|
*
|
||||||
|
* @author xiaoleilu
|
||||||
|
*/
|
||||||
|
public class HtmlUtil {
|
||||||
|
|
||||||
|
public static final String NBSP = StrUtil.HTML_NBSP;
|
||||||
|
public static final String AMP = StrUtil.HTML_AMP;
|
||||||
|
public static final String QUOTE = StrUtil.HTML_QUOTE;
|
||||||
|
public static final String APOS = StrUtil.HTML_APOS;
|
||||||
|
public static final String LT = StrUtil.HTML_LT;
|
||||||
|
public static final String GT = StrUtil.HTML_GT;
|
||||||
|
|
||||||
|
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
|
||||||
|
public static final String RE_SCRIPT = "<[\\s]*?script[^>]*?>.*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
|
||||||
|
|
||||||
|
private static final char[][] TEXT = new char[256][];
|
||||||
|
|
||||||
|
static {
|
||||||
|
// ascii码值最大的是【0x7f=127】,扩展ascii码值最大的是【0xFF=255】,因为ASCII码使用指定的7位或8位二进制数组合来表示128或256种可能的字符,标准ASCII码也叫基础ASCII码。
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
TEXT[i] = new char[]{(char) i};
|
||||||
|
}
|
||||||
|
|
||||||
|
// special HTML characters
|
||||||
|
TEXT['\''] = "'".toCharArray(); // 单引号 (''' doesn't work - it is not by the w3 specs)
|
||||||
|
TEXT['"'] = QUOTE.toCharArray(); // 双引号
|
||||||
|
TEXT['&'] = AMP.toCharArray(); // &符
|
||||||
|
TEXT['<'] = LT.toCharArray(); // 小于号
|
||||||
|
TEXT['>'] = GT.toCharArray(); // 大于号
|
||||||
|
TEXT[' '] = NBSP.toCharArray(); // 不断开空格(non-breaking space,缩写nbsp。ASCII值是32:是用键盘输入的空格;ASCII值是160:不间断空格,即  ,所产生的空格,作用是在页面换行时不被打断)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义文本中的HTML字符为安全的字符,以下字符被转义:
|
||||||
|
* <ul>
|
||||||
|
* <li>' 替换为 &#039; (&apos; doesn't work in HTML4)</li>
|
||||||
|
* <li>" 替换为 &quot;</li>
|
||||||
|
* <li>& 替换为 &amp;</li>
|
||||||
|
* <li>< 替换为 &lt;</li>
|
||||||
|
* <li>> 替换为 &gt;</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param text 被转义的文本
|
||||||
|
* @return 转义后的文本
|
||||||
|
*/
|
||||||
|
public static String escape(String text) {
|
||||||
|
return encode(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 还原被转义的HTML特殊字符
|
||||||
|
*
|
||||||
|
* @param htmlStr 包含转义符的HTML内容
|
||||||
|
* @return 转换后的字符串
|
||||||
|
*/
|
||||||
|
public static String unescape(String htmlStr) {
|
||||||
|
if (StrUtil.isBlank(htmlStr)) {
|
||||||
|
return htmlStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EscapeUtil.unescapeHtml4(htmlStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- encode text
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有HTML标签,但是不删除标签内的内容
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @return 清除标签后的文本
|
||||||
|
*/
|
||||||
|
public static String cleanHtmlTag(String content) {
|
||||||
|
return content.replaceAll(RE_HTML_MARK, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定HTML标签和被标签包围的内容<br>
|
||||||
|
* 不区分大小写
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @param tagNames 要清除的标签
|
||||||
|
* @return 去除标签后的文本
|
||||||
|
*/
|
||||||
|
public static String removeHtmlTag(String content, String... tagNames) {
|
||||||
|
return removeHtmlTag(content, true, tagNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定HTML标签,不包括内容<br>
|
||||||
|
* 不区分大小写
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @param tagNames 要清除的标签
|
||||||
|
* @return 去除标签后的文本
|
||||||
|
*/
|
||||||
|
public static String unwrapHtmlTag(String content, String... tagNames) {
|
||||||
|
return removeHtmlTag(content, false, tagNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除指定HTML标签<br>
|
||||||
|
* 不区分大小写
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @param withTagContent 是否去掉被包含在标签中的内容
|
||||||
|
* @param tagNames 要清除的标签
|
||||||
|
* @return 去除标签后的文本
|
||||||
|
*/
|
||||||
|
public static String removeHtmlTag(String content, boolean withTagContent, String... tagNames) {
|
||||||
|
String regex;
|
||||||
|
for (String tagName : tagNames) {
|
||||||
|
if (StrUtil.isBlank(tagName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tagName = tagName.trim();
|
||||||
|
// (?i)表示其后面的表达式忽略大小写
|
||||||
|
if (withTagContent) {
|
||||||
|
// 标签及其包含内容
|
||||||
|
regex = StrUtil.format("(?i)<{}(\\s+[^>]*?)?/?>(.*?</{}>)?", tagName, tagName);
|
||||||
|
} else {
|
||||||
|
// 标签不包含内容
|
||||||
|
regex = StrUtil.format("(?i)<{}(\\s+[^>]*?)?/?>|</?{}>", tagName, tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
content = ReUtil.delAll(regex, content); // 非自闭标签小写
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除HTML标签中的属性,如果多个标签有相同属性,都去除
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @param attrs 属性名(不区分大小写)
|
||||||
|
* @return 处理后的文本
|
||||||
|
*/
|
||||||
|
public static String removeHtmlAttr(String content, String... attrs) {
|
||||||
|
String regex;
|
||||||
|
for (String attr : attrs) {
|
||||||
|
// (?i) 表示忽略大小写
|
||||||
|
// \s* 属性名前后的空白符去除
|
||||||
|
// [^>]+? 属性值,至少有一个非>的字符,>表示标签结束
|
||||||
|
// \s+(?=>) 表示属性值后跟空格加>,即末尾的属性,此时去掉空格
|
||||||
|
// (?=\s|>) 表示属性值后跟空格(属性后还有别的属性)或者跟>(最后一个属性)
|
||||||
|
regex = StrUtil.format("(?i)(\\s*{}\\s*=[^>]+?\\s+(?=>))|(\\s*{}\\s*=[^>]+?(?=\\s|>))", attr, attr);
|
||||||
|
content = content.replaceAll(regex, StrUtil.EMPTY);
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去除指定标签的所有属性
|
||||||
|
*
|
||||||
|
* @param content 内容
|
||||||
|
* @param tagNames 指定标签
|
||||||
|
* @return 处理后的文本
|
||||||
|
*/
|
||||||
|
public static String removeAllHtmlAttr(String content, String... tagNames) {
|
||||||
|
String regex;
|
||||||
|
for (String tagName : tagNames) {
|
||||||
|
regex = StrUtil.format("(?i)<{}[^>]*?>", tagName);
|
||||||
|
content = content.replaceAll(regex, StrUtil.format("<{}>", tagName));
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoder
|
||||||
|
*
|
||||||
|
* @param text 被编码的文本
|
||||||
|
* @return 编码后的字符
|
||||||
|
*/
|
||||||
|
private static String encode(String text) {
|
||||||
|
int len;
|
||||||
|
if ((text == null) || ((len = text.length()) == 0)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
StringBuilder buffer = new StringBuilder(len + (len >> 2));
|
||||||
|
char c;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
c = text.charAt(i);
|
||||||
|
if (c < 256) {
|
||||||
|
buffer.append(TEXT[c]);
|
||||||
|
} else {
|
||||||
|
buffer.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤HTML文本,防止XSS攻击
|
||||||
|
*
|
||||||
|
* @param htmlContent HTML内容
|
||||||
|
* @return 过滤后的内容
|
||||||
|
*/
|
||||||
|
public static String filter(String htmlContent) {
|
||||||
|
return new HTMLFilter().filter(htmlContent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharsetUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http基类
|
||||||
|
*
|
||||||
|
* @param <T> 子类类型,方便链式编程
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public abstract class HttpBase<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的请求编码、URL的encode、decode编码
|
||||||
|
*/
|
||||||
|
protected static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP/1.0
|
||||||
|
*/
|
||||||
|
public static final String HTTP_1_0 = "HTTP/1.0";
|
||||||
|
/**
|
||||||
|
* HTTP/1.1
|
||||||
|
*/
|
||||||
|
public static final String HTTP_1_1 = "HTTP/1.1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储头信息
|
||||||
|
*/
|
||||||
|
protected Map<String, List<String>> headers = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
protected Charset charset = DEFAULT_CHARSET;
|
||||||
|
/**
|
||||||
|
* http版本
|
||||||
|
*/
|
||||||
|
protected String httpVersion = HTTP_1_1;
|
||||||
|
/**
|
||||||
|
* 存储主体
|
||||||
|
*/
|
||||||
|
protected byte[] bodyBytes;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Headers start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息<br>
|
||||||
|
* 根据RFC2616规范,header的name不区分大小写
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
public String header(String name) {
|
||||||
|
final List<String> values = headerList(name);
|
||||||
|
if (CollectionUtil.isEmpty(values)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return values.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息列表
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
public List<String> headerList(String name) {
|
||||||
|
if (StrUtil.isBlank(name)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CaseInsensitiveMap<String, List<String>> headersIgnoreCase = new CaseInsensitiveMap<>(this.headers);
|
||||||
|
return headersIgnoreCase.get(name.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据name获取头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Header值
|
||||||
|
*/
|
||||||
|
public String header(Header name) {
|
||||||
|
if (null == name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return header(name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @param isOverride 是否覆盖已有值
|
||||||
|
* @return T 本身
|
||||||
|
*/
|
||||||
|
public T header(String name, String value, boolean isOverride) {
|
||||||
|
if (null != name && null != value) {
|
||||||
|
final List<String> values = headers.get(name.trim());
|
||||||
|
if (isOverride || CollectionUtil.isEmpty(values)) {
|
||||||
|
final ArrayList<String> valueList = new ArrayList<>();
|
||||||
|
valueList.add(value);
|
||||||
|
headers.put(name.trim(), valueList);
|
||||||
|
} else {
|
||||||
|
values.add(value.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @param isOverride 是否覆盖已有值
|
||||||
|
* @return T 本身
|
||||||
|
*/
|
||||||
|
public T header(Header name, String value, boolean isOverride) {
|
||||||
|
return header(name.toString(), value, isOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 覆盖模式,则替换之前的值
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @return T 本身
|
||||||
|
*/
|
||||||
|
public T header(Header name, String value) {
|
||||||
|
return header(name.toString(), value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个header<br>
|
||||||
|
* 覆盖模式,则替换之前的值
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @param value Header值
|
||||||
|
* @return T 本身
|
||||||
|
*/
|
||||||
|
public T header(String name, String value) {
|
||||||
|
return header(name, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头
|
||||||
|
*
|
||||||
|
* @param headers 请求头
|
||||||
|
* @param isOverride 是否覆盖已有头信息
|
||||||
|
* @return this
|
||||||
|
* @since 4.6.3
|
||||||
|
*/
|
||||||
|
public T headerMap(Map<String, String> headers, boolean isOverride) {
|
||||||
|
if (MapUtil.isEmpty(headers)) {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
this.header(entry.getKey(), StrUtil.nullToEmpty(entry.getValue()), isOverride);
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头<br>
|
||||||
|
* 不覆盖原有请求头
|
||||||
|
*
|
||||||
|
* @param headers 请求头
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public T header(Map<String, List<String>> headers) {
|
||||||
|
return header(headers, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头
|
||||||
|
*
|
||||||
|
* @param headers 请求头
|
||||||
|
* @param isOverride 是否覆盖已有头信息
|
||||||
|
* @return this
|
||||||
|
* @since 4.0.8
|
||||||
|
*/
|
||||||
|
public T header(Map<String, List<String>> headers, boolean isOverride) {
|
||||||
|
if (MapUtil.isEmpty(headers)) {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name;
|
||||||
|
for (Entry<String, List<String>> entry : headers.entrySet()) {
|
||||||
|
name = entry.getKey();
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
this.header(name, StrUtil.nullToEmpty(value), isOverride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增请求头<br>
|
||||||
|
* 不覆盖原有请求头
|
||||||
|
*
|
||||||
|
* @param headers 请求头
|
||||||
|
* @return this
|
||||||
|
* @since 4.0.3
|
||||||
|
*/
|
||||||
|
public T addHeaders(Map<String, String> headers) {
|
||||||
|
if (MapUtil.isEmpty(headers)) {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entry<String, String> entry : headers.entrySet()) {
|
||||||
|
this.header(entry.getKey(), StrUtil.nullToEmpty(entry.getValue()), false);
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public T removeHeader(String name) {
|
||||||
|
if (name != null) {
|
||||||
|
headers.remove(name.trim());
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个头信息
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public T removeHeader(Header name) {
|
||||||
|
return removeHeader(name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取headers
|
||||||
|
*
|
||||||
|
* @return Headers Map
|
||||||
|
*/
|
||||||
|
public Map<String, List<String>> headers() {
|
||||||
|
return Collections.unmodifiableMap(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有头信息,包括全局头信息
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.13
|
||||||
|
*/
|
||||||
|
public T clearHeaders() {
|
||||||
|
this.headers.clear();
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------- Headers end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回http版本
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String httpVersion() {
|
||||||
|
return httpVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置http版本,此方法不会影响到实际请求的HTTP版本,只用于帮助判断是否connect:Keep-Alive
|
||||||
|
*
|
||||||
|
* @param httpVersion Http版本,{@link HttpBase#HTTP_1_0},{@link HttpBase#HTTP_1_1}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public T httpVersion(String httpVersion) {
|
||||||
|
this.httpVersion = httpVersion;
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取bodyBytes存储字节码
|
||||||
|
*
|
||||||
|
* @return byte[]
|
||||||
|
*/
|
||||||
|
public byte[] bodyBytes() {
|
||||||
|
return this.bodyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回字符集
|
||||||
|
*
|
||||||
|
* @return 字符集
|
||||||
|
*/
|
||||||
|
public String charset() {
|
||||||
|
return charset.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字符集
|
||||||
|
*
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return T 自己
|
||||||
|
* @see CharsetUtil
|
||||||
|
*/
|
||||||
|
public T charset(String charset) {
|
||||||
|
if (StrUtil.isNotBlank(charset)) {
|
||||||
|
charset(Charset.forName(charset));
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字符集
|
||||||
|
*
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return T 自己
|
||||||
|
* @see CharsetUtil
|
||||||
|
*/
|
||||||
|
public T charset(Charset charset) {
|
||||||
|
if (null != charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = StrUtil.builder();
|
||||||
|
sb.append("Request Headers: ").append(StrUtil.CRLF);
|
||||||
|
for (Entry<String, List<String>> entry : this.headers.entrySet()) {
|
||||||
|
sb.append(" ").append(
|
||||||
|
entry.getKey()).append(": ").append(CollUtil.join(entry.getValue(), ","))
|
||||||
|
.append(StrUtil.CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("Request Body: ").append(StrUtil.CRLF);
|
||||||
|
sb.append(" ").append(StrUtil.str(this.bodyBytes, this.charset)).append(StrUtil.CRLF);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.SSLUtil;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http配置项
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public class HttpConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建默认Http配置信息
|
||||||
|
*
|
||||||
|
* @return HttpConfig
|
||||||
|
*/
|
||||||
|
public static HttpConfig create() {
|
||||||
|
return new HttpConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认连接超时
|
||||||
|
*/
|
||||||
|
int connectionTimeout = HttpGlobalConfig.getTimeout();
|
||||||
|
/**
|
||||||
|
* 默认读取超时
|
||||||
|
*/
|
||||||
|
int readTimeout = HttpGlobalConfig.getTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否禁用缓存
|
||||||
|
*/
|
||||||
|
boolean isDisableCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大重定向次数
|
||||||
|
*/
|
||||||
|
int maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理
|
||||||
|
*/
|
||||||
|
Proxy proxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HostnameVerifier,用于HTTPS安全连接
|
||||||
|
*/
|
||||||
|
HostnameVerifier hostnameVerifier;
|
||||||
|
/**
|
||||||
|
* SSLSocketFactory,用于HTTPS安全连接
|
||||||
|
*/
|
||||||
|
SSLSocketFactory ssf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chuncked块大小,0或小于0表示不设置Chuncked模式
|
||||||
|
*/
|
||||||
|
int blockSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否忽略响应读取时可能的EOF异常。<br>
|
||||||
|
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。<br>
|
||||||
|
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
|
||||||
|
*/
|
||||||
|
boolean ignoreEOFError = HttpGlobalConfig.isIgnoreEOFError();
|
||||||
|
/**
|
||||||
|
* 获取是否忽略解码URL,包括URL中的Path部分和Param部分。<br>
|
||||||
|
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果此参数为{@code true},则会统一解码编码后的参数,<br>
|
||||||
|
* 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
|
||||||
|
*/
|
||||||
|
boolean decodeUrl = HttpGlobalConfig.isDecodeUrl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求前的拦截器,用于在请求前重新编辑请求
|
||||||
|
*/
|
||||||
|
final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpRequest> requestInterceptors = GlobalInterceptor.INSTANCE.getCopiedRequestInterceptor();
|
||||||
|
/**
|
||||||
|
* 响应后的拦截器,用于在响应后处理逻辑
|
||||||
|
*/
|
||||||
|
final HttpInterceptor.Chain<aiyh.utils.tool.cn.hutool.http.HttpResponse> responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重定向时是否使用拦截器
|
||||||
|
*/
|
||||||
|
boolean interceptorOnRedirect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置超时,单位:毫秒<br>
|
||||||
|
* 超时包括:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 连接超时
|
||||||
|
* 2. 读取响应超时
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
* @see #setConnectionTimeout(int)
|
||||||
|
* @see #setReadTimeout(int)
|
||||||
|
*/
|
||||||
|
public HttpConfig timeout(int milliseconds) {
|
||||||
|
setConnectionTimeout(milliseconds);
|
||||||
|
setReadTimeout(milliseconds);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接超时,单位:毫秒
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setConnectionTimeout(int milliseconds) {
|
||||||
|
this.connectionTimeout = milliseconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接超时,单位:毫秒
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setReadTimeout(int milliseconds) {
|
||||||
|
this.readTimeout = milliseconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用缓存
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig disableCache() {
|
||||||
|
this.isDisableCache = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最大重定向次数<br>
|
||||||
|
* 如果次数小于1则表示不重定向,大于等于1表示打开重定向
|
||||||
|
*
|
||||||
|
* @param maxRedirectCount 最大重定向次数
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setMaxRedirectCount(int maxRedirectCount) {
|
||||||
|
this.maxRedirectCount = Math.max(maxRedirectCount, 0);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置域名验证器<br>
|
||||||
|
* 只针对HTTPS请求,如果不设置,不做验证,所有域名被信任
|
||||||
|
*
|
||||||
|
* @param hostnameVerifier HostnameVerifier
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setHostnameVerifier(HostnameVerifier hostnameVerifier) {
|
||||||
|
// 验证域
|
||||||
|
this.hostnameVerifier = hostnameVerifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Http代理
|
||||||
|
*
|
||||||
|
* @param host 代理 主机
|
||||||
|
* @param port 代理 端口
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setHttpProxy(String host, int port) {
|
||||||
|
final Proxy proxy = new Proxy(Proxy.Type.HTTP,
|
||||||
|
new InetSocketAddress(host, port));
|
||||||
|
return setProxy(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置代理
|
||||||
|
*
|
||||||
|
* @param proxy 代理 {@link Proxy}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setProxy(Proxy proxy) {
|
||||||
|
this.proxy = proxy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置SSLSocketFactory<br>
|
||||||
|
* 只针对HTTPS请求,如果不设置,使用默认的SSLSocketFactory<br>
|
||||||
|
* 默认SSLSocketFactory为:SSLSocketFactoryBuilder.create().build();
|
||||||
|
*
|
||||||
|
* @param ssf SSLScketFactory
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setSSLSocketFactory(SSLSocketFactory ssf) {
|
||||||
|
this.ssf = ssf;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置HTTPS安全连接协议,只针对HTTPS请求,可以使用的协议包括:<br>
|
||||||
|
* 此方法调用后{@link #setSSLSocketFactory(SSLSocketFactory)} 将被覆盖。
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. TLSv1.2
|
||||||
|
* 2. TLSv1.1
|
||||||
|
* 3. SSLv3
|
||||||
|
* ...
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param protocol 协议
|
||||||
|
* @return this
|
||||||
|
* @see SSLUtil#createSSLContext(String)
|
||||||
|
* @see #setSSLSocketFactory(SSLSocketFactory)
|
||||||
|
*/
|
||||||
|
public HttpConfig setSSLProtocol(String protocol) {
|
||||||
|
Assert.notBlank(protocol, "protocol must be not blank!");
|
||||||
|
setSSLSocketFactory(SSLUtil.createSSLContext(protocol).getSocketFactory());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采用流方式上传数据,无需本地缓存数据。<br>
|
||||||
|
* HttpUrlConnection默认是将所有数据读到本地缓存,然后再发送给服务器,这样上传大文件时就会导致内存溢出。
|
||||||
|
*
|
||||||
|
* @param blockSize 块大小(bytes数),0或小于0表示不设置Chuncked模式
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setBlockSize(int blockSize) {
|
||||||
|
this.blockSize = blockSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略响应读取时可能的EOF异常。<br>
|
||||||
|
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。<br>
|
||||||
|
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
|
||||||
|
*
|
||||||
|
* @param ignoreEOFError 是否忽略响应读取时可能的EOF异常。
|
||||||
|
* @return this
|
||||||
|
* @since 5.7.20
|
||||||
|
*/
|
||||||
|
public HttpConfig setIgnoreEOFError(boolean ignoreEOFError) {
|
||||||
|
this.ignoreEOFError = ignoreEOFError;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略解码URL,包括URL中的Path部分和Param部分。<br>
|
||||||
|
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果此参数为{@code true},则会统一解码编码后的参数,<br>
|
||||||
|
* 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
|
||||||
|
*
|
||||||
|
* @param decodeUrl 是否忽略解码URL
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setDecodeUrl(boolean decodeUrl) {
|
||||||
|
this.decodeUrl = decodeUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置拦截器,用于在请求前重新编辑请求
|
||||||
|
*
|
||||||
|
* @param interceptor 拦截器实现
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig addRequestInterceptor(HttpInterceptor<HttpRequest> interceptor) {
|
||||||
|
this.requestInterceptors.addChain(interceptor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置拦截器,用于在请求前重新编辑请求
|
||||||
|
*
|
||||||
|
* @param interceptor 拦截器实现
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig addResponseInterceptor(HttpInterceptor<HttpResponse> interceptor) {
|
||||||
|
this.responseInterceptors.addChain(interceptor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重定向时是否使用拦截器
|
||||||
|
*
|
||||||
|
* @param interceptorOnRedirect 重定向时是否使用拦截器
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConfig setInterceptorOnRedirect(boolean interceptorOnRedirect) {
|
||||||
|
this.interceptorOnRedirect = interceptorOnRedirect;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,568 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.URLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.ssl.DefaultSSLInfo;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http连接对象,对HttpURLConnection的包装
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public class HttpConnection {
|
||||||
|
|
||||||
|
private final URL url;
|
||||||
|
private final Proxy proxy;
|
||||||
|
private HttpURLConnection conn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建HttpConnection
|
||||||
|
*
|
||||||
|
* @param urlStr URL
|
||||||
|
* @param proxy 代理,无代理传{@code null}
|
||||||
|
* @return HttpConnection
|
||||||
|
*/
|
||||||
|
public static HttpConnection create(String urlStr, Proxy proxy) {
|
||||||
|
return create(URLUtil.toUrlForHttp(urlStr), proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建HttpConnection
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param proxy 代理,无代理传{@code null}
|
||||||
|
* @return HttpConnection
|
||||||
|
*/
|
||||||
|
public static HttpConnection create(URL url, Proxy proxy) {
|
||||||
|
return new HttpConnection(url, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------- Constructor start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造HttpConnection
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param proxy 代理
|
||||||
|
*/
|
||||||
|
public HttpConnection(URL url, Proxy proxy) {
|
||||||
|
this.url = url;
|
||||||
|
this.proxy = proxy;
|
||||||
|
|
||||||
|
// 初始化Http连接
|
||||||
|
initConn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化连接相关信息
|
||||||
|
*
|
||||||
|
* @return HttpConnection
|
||||||
|
* @since 4.4.1
|
||||||
|
*/
|
||||||
|
public HttpConnection initConn() {
|
||||||
|
try {
|
||||||
|
this.conn = openHttp();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认读取响应内容
|
||||||
|
this.conn.setDoInput(true);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------- Getters And Setters start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求方法,GET/POST
|
||||||
|
*
|
||||||
|
* @return 请求方法, GET/POST
|
||||||
|
*/
|
||||||
|
public aiyh.utils.tool.cn.hutool.http.Method getMethod() {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.Method.valueOf(this.conn.getRequestMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法
|
||||||
|
*
|
||||||
|
* @param method 请求方法
|
||||||
|
* @return 自己
|
||||||
|
*/
|
||||||
|
public HttpConnection setMethod(aiyh.utils.tool.cn.hutool.http.Method method) {
|
||||||
|
if (aiyh.utils.tool.cn.hutool.http.Method.POST.equals(method) //
|
||||||
|
|| aiyh.utils.tool.cn.hutool.http.Method.PUT.equals(method)//
|
||||||
|
|| aiyh.utils.tool.cn.hutool.http.Method.PATCH.equals(method)//
|
||||||
|
|| aiyh.utils.tool.cn.hutool.http.Method.DELETE.equals(method)) {
|
||||||
|
this.conn.setUseCaches(false);
|
||||||
|
|
||||||
|
// 增加PATCH方法支持
|
||||||
|
if (aiyh.utils.tool.cn.hutool.http.Method.PATCH.equals(method)) {
|
||||||
|
try {
|
||||||
|
HttpGlobalConfig.allowPatch();
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// ignore
|
||||||
|
// https://github.com/dromara/hutool/issues/2832
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method
|
||||||
|
try {
|
||||||
|
this.conn.setRequestMethod(method.toString());
|
||||||
|
} catch (ProtocolException e) {
|
||||||
|
if (aiyh.utils.tool.cn.hutool.http.Method.PATCH.equals(method)) {
|
||||||
|
// 如果全局设置失效,此处针对单独链接重新设置
|
||||||
|
reflectSetMethod(method);
|
||||||
|
} else {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求URL
|
||||||
|
*
|
||||||
|
* @return 请求URL
|
||||||
|
*/
|
||||||
|
public URL getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得代理
|
||||||
|
*
|
||||||
|
* @return {@link Proxy}
|
||||||
|
*/
|
||||||
|
public Proxy getProxy() {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取HttpURLConnection对象
|
||||||
|
*
|
||||||
|
* @return HttpURLConnection
|
||||||
|
*/
|
||||||
|
public HttpURLConnection getHttpURLConnection() {
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------- Getters And Setters end
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Headers start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头<br>
|
||||||
|
* 当请求头存在时,覆盖之
|
||||||
|
*
|
||||||
|
* @param header 头名
|
||||||
|
* @param value 头值
|
||||||
|
* @param isOverride 是否覆盖旧值
|
||||||
|
* @return HttpConnection
|
||||||
|
*/
|
||||||
|
public HttpConnection header(String header, String value, boolean isOverride) {
|
||||||
|
if (null != this.conn) {
|
||||||
|
if (isOverride) {
|
||||||
|
this.conn.setRequestProperty(header, value);
|
||||||
|
} else {
|
||||||
|
this.conn.addRequestProperty(header, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头<br>
|
||||||
|
* 当请求头存在时,覆盖之
|
||||||
|
*
|
||||||
|
* @param header 头名
|
||||||
|
* @param value 头值
|
||||||
|
* @param isOverride 是否覆盖旧值
|
||||||
|
* @return HttpConnection
|
||||||
|
*/
|
||||||
|
public HttpConnection header(aiyh.utils.tool.cn.hutool.http.Header header, String value, boolean isOverride) {
|
||||||
|
return header(header.toString(), value, isOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求头<br>
|
||||||
|
* 不覆盖原有请求头
|
||||||
|
*
|
||||||
|
* @param headerMap 请求头
|
||||||
|
* @param isOverride 是否覆盖
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection header(Map<String, List<String>> headerMap, boolean isOverride) {
|
||||||
|
if (MapUtil.isNotEmpty(headerMap)) {
|
||||||
|
String name;
|
||||||
|
for (Entry<String, List<String>> entry : headerMap.entrySet()) {
|
||||||
|
name = entry.getKey();
|
||||||
|
for (String value : entry.getValue()) {
|
||||||
|
this.header(name, StrUtil.nullToEmpty(value), isOverride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Http请求头
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Http请求头值
|
||||||
|
*/
|
||||||
|
public String header(String name) {
|
||||||
|
return this.conn.getHeaderField(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Http请求头
|
||||||
|
*
|
||||||
|
* @param name Header名
|
||||||
|
* @return Http请求头值
|
||||||
|
*/
|
||||||
|
public String header(aiyh.utils.tool.cn.hutool.http.Header name) {
|
||||||
|
return header(name.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有Http请求头
|
||||||
|
*
|
||||||
|
* @return Http请求头Map
|
||||||
|
*/
|
||||||
|
public Map<String, List<String>> headers() {
|
||||||
|
return this.conn.getHeaderFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Headers end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置https请求参数<br>
|
||||||
|
* 有些时候htts请求会出现com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl的实现,此为sun内部api,按照普通http请求处理
|
||||||
|
*
|
||||||
|
* @param hostnameVerifier 域名验证器,非https传入null
|
||||||
|
* @param ssf SSLSocketFactory,非https传入null
|
||||||
|
* @return this
|
||||||
|
* @throws HttpException KeyManagementException和NoSuchAlgorithmException异常包装
|
||||||
|
*/
|
||||||
|
public HttpConnection setHttpsInfo(HostnameVerifier hostnameVerifier, SSLSocketFactory ssf) throws HttpException {
|
||||||
|
final HttpURLConnection conn = this.conn;
|
||||||
|
|
||||||
|
if (conn instanceof HttpsURLConnection) {
|
||||||
|
// Https请求
|
||||||
|
final HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
|
||||||
|
// 验证域
|
||||||
|
httpsConn.setHostnameVerifier(ObjectUtil.defaultIfNull(hostnameVerifier, DefaultSSLInfo.TRUST_ANY_HOSTNAME_VERIFIER));
|
||||||
|
httpsConn.setSSLSocketFactory(ObjectUtil.defaultIfNull(ssf, DefaultSSLInfo.DEFAULT_SSF));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭缓存
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @see HttpURLConnection#setUseCaches(boolean)
|
||||||
|
*/
|
||||||
|
public HttpConnection disableCache() {
|
||||||
|
this.conn.setUseCaches(false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接超时
|
||||||
|
*
|
||||||
|
* @param timeout 超时
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setConnectTimeout(int timeout) {
|
||||||
|
if (timeout > 0 && null != this.conn) {
|
||||||
|
this.conn.setConnectTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置读取超时
|
||||||
|
*
|
||||||
|
* @param timeout 超时
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setReadTimeout(int timeout) {
|
||||||
|
if (timeout > 0 && null != this.conn) {
|
||||||
|
this.conn.setReadTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接和读取的超时时间
|
||||||
|
*
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setConnectionAndReadTimeout(int timeout) {
|
||||||
|
setConnectTimeout(timeout);
|
||||||
|
setReadTimeout(timeout);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Cookie
|
||||||
|
*
|
||||||
|
* @param cookie Cookie
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setCookie(String cookie) {
|
||||||
|
if (cookie != null) {
|
||||||
|
header(Header.COOKIE, cookie, true);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 采用流方式上传数据,无需本地缓存数据。<br>
|
||||||
|
* HttpUrlConnection默认是将所有数据读到本地缓存,然后再发送给服务器,这样上传大文件时就会导致内存溢出。
|
||||||
|
*
|
||||||
|
* @param blockSize 块大小(bytes数),0或小于0表示不设置Chuncked模式
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setChunkedStreamingMode(int blockSize) {
|
||||||
|
if (blockSize > 0) {
|
||||||
|
conn.setChunkedStreamingMode(blockSize);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置自动HTTP 30X跳转
|
||||||
|
*
|
||||||
|
* @param isInstanceFollowRedirects 是否自定跳转
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection setInstanceFollowRedirects(boolean isInstanceFollowRedirects) {
|
||||||
|
conn.setInstanceFollowRedirects(isInstanceFollowRedirects);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public HttpConnection connect() throws IOException {
|
||||||
|
if (null != this.conn) {
|
||||||
|
this.conn.connect();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 静默断开连接。不抛出异常
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 4.6.0
|
||||||
|
*/
|
||||||
|
public HttpConnection disconnectQuietly() {
|
||||||
|
try {
|
||||||
|
disconnect();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开连接
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpConnection disconnect() {
|
||||||
|
if (null != this.conn) {
|
||||||
|
this.conn.disconnect();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得输入流对象<br>
|
||||||
|
* 输入流对象用于读取数据
|
||||||
|
*
|
||||||
|
* @return 输入流对象
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
if (null != this.conn) {
|
||||||
|
return this.conn.getInputStream();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当返回错误代码时,获得错误内容流
|
||||||
|
*
|
||||||
|
* @return 错误内容
|
||||||
|
*/
|
||||||
|
public InputStream getErrorStream() {
|
||||||
|
if (null != this.conn) {
|
||||||
|
return this.conn.getErrorStream();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取输出流对象 输出流对象用于发送数据
|
||||||
|
*
|
||||||
|
* @return OutputStream
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public OutputStream getOutputStream() throws IOException {
|
||||||
|
if (null == this.conn) {
|
||||||
|
throw new IOException("HttpURLConnection has not been initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.Method method = getMethod();
|
||||||
|
|
||||||
|
// 当有写出需求时,自动打开之
|
||||||
|
this.conn.setDoOutput(true);
|
||||||
|
final OutputStream out = this.conn.getOutputStream();
|
||||||
|
|
||||||
|
// 解决在Rest请求中,GET请求附带body导致GET请求被强制转换为POST
|
||||||
|
// 在sun.net.www.protocol.http.HttpURLConnection.getOutputStream0方法中,会把GET方法
|
||||||
|
// 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
|
||||||
|
// https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
|
||||||
|
if (method == aiyh.utils.tool.cn.hutool.http.Method.GET && method != getMethod()) {
|
||||||
|
reflectSetMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应码
|
||||||
|
*
|
||||||
|
* @return 响应码
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public int responseCode() throws IOException {
|
||||||
|
if (null != this.conn) {
|
||||||
|
return this.conn.getResponseCode();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得字符集编码<br>
|
||||||
|
* 从Http连接的头信息中获得字符集<br>
|
||||||
|
* 从ContentType中获取
|
||||||
|
*
|
||||||
|
* @return 字符集编码
|
||||||
|
*/
|
||||||
|
public String getCharsetName() {
|
||||||
|
return HttpUtil.getCharset(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字符集编码<br>
|
||||||
|
* 从Http连接的头信息中获得字符集<br>
|
||||||
|
* 从ContentType中获取
|
||||||
|
*
|
||||||
|
* @return {@link Charset}编码
|
||||||
|
* @since 3.0.9
|
||||||
|
*/
|
||||||
|
public Charset getCharset() {
|
||||||
|
Charset charset = null;
|
||||||
|
final String charsetName = getCharsetName();
|
||||||
|
if (StrUtil.isNotBlank(charsetName)) {
|
||||||
|
try {
|
||||||
|
charset = Charset.forName(charsetName);
|
||||||
|
} catch (UnsupportedCharsetException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = StrUtil.builder();
|
||||||
|
sb.append("Request URL: ").append(this.url).append(StrUtil.CRLF);
|
||||||
|
sb.append("Request Method: ").append(this.getMethod()).append(StrUtil.CRLF);
|
||||||
|
// sb.append("Request Headers: ").append(StrUtil.CRLF);
|
||||||
|
// for (Entry<String, List<String>> entry : this.conn.getHeaderFields().entrySet()) {
|
||||||
|
// sb.append(" ").append(entry).append(StrUtil.CRLF);
|
||||||
|
// }
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------- Private Method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化http或https请求参数<br>
|
||||||
|
* 有些时候https请求会出现com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl的实现,此为sun内部api,按照普通http请求处理
|
||||||
|
*
|
||||||
|
* @return {@link HttpURLConnection},https返回{@link HttpsURLConnection}
|
||||||
|
*/
|
||||||
|
private HttpURLConnection openHttp() throws IOException {
|
||||||
|
final URLConnection conn = openConnection();
|
||||||
|
if (!(conn instanceof HttpURLConnection)) {
|
||||||
|
// 防止其它协议造成的转换异常
|
||||||
|
throw new HttpException("'{}' of URL [{}] is not a http connection, make sure URL is format for http.", conn.getClass().getName(), this.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (HttpURLConnection) conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建立连接
|
||||||
|
*
|
||||||
|
* @return {@link URLConnection}
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
private URLConnection openConnection() throws IOException {
|
||||||
|
return (null == this.proxy) ? url.openConnection() : url.openConnection(this.proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过反射设置方法名,首先设置HttpURLConnection本身的方法名,再检查是否为代理类,如果是,设置带路对象的方法名。
|
||||||
|
*
|
||||||
|
* @param method 方法名
|
||||||
|
*/
|
||||||
|
private void reflectSetMethod(Method method) {
|
||||||
|
ReflectUtil.setFieldValue(this.conn, "method", method.name());
|
||||||
|
|
||||||
|
// HttpsURLConnectionImpl实现中,使用了代理类,需要修改被代理类的method方法
|
||||||
|
final Object delegate = ReflectUtil.getFieldValue(this.conn, "delegate");
|
||||||
|
if (null != delegate) {
|
||||||
|
ReflectUtil.setFieldValue(delegate, "method", method.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --------------------------------------------------------------- Private Method end
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FastByteArrayOutputStream;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.StreamProgress;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载封装,下载统一使用{@code GET}请求,默认支持30x跳转
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.6.4
|
||||||
|
*/
|
||||||
|
public class HttpDownloader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文本
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param customCharset 自定义的字符集,可以使用{@code CharsetUtil#charset} 方法转换
|
||||||
|
* @param streamPress 进度条 {@link StreamProgress}
|
||||||
|
* @return 文本
|
||||||
|
*/
|
||||||
|
public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) {
|
||||||
|
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
|
||||||
|
download(url, out, true, streamPress);
|
||||||
|
return null == customCharset ? out.toString() : out.toString(customCharset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件数据,支持30x跳转
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @return 文件数据
|
||||||
|
*/
|
||||||
|
public static byte[] downloadBytes(String url) {
|
||||||
|
return requestDownload(url, -1).bodyBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, File targetFileOrDir, int timeout, StreamProgress streamProgress) {
|
||||||
|
return requestDownload(url, timeout).writeBody(targetFileOrDir, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件-避免未完成的文件<br>
|
||||||
|
* 来自:https://gitee.com/dromara/hutool/pulls/407<br>
|
||||||
|
* 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param tempFileSuffix 临时文件后缀,默认".temp"
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 下载大小
|
||||||
|
* @since 5.7.12
|
||||||
|
*/
|
||||||
|
public long downloadFile(String url, File targetFileOrDir, String tempFileSuffix, int timeout, StreamProgress streamProgress) {
|
||||||
|
return requestDownload(url, timeout).writeBody(targetFileOrDir, tempFileSuffix, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件,返回文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件
|
||||||
|
*/
|
||||||
|
public static File downloadForFile(String url, File targetFileOrDir, int timeout, StreamProgress streamProgress) {
|
||||||
|
return requestDownload(url, timeout).writeBodyForFile(targetFileOrDir, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||||
|
* @param isCloseOut 是否关闭输出流
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long download(String url, OutputStream out, boolean isCloseOut, StreamProgress streamProgress) {
|
||||||
|
Assert.notNull(out, "[out] is null !");
|
||||||
|
|
||||||
|
return requestDownload(url, -1).writeBody(out, isCloseOut, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求下载文件
|
||||||
|
*
|
||||||
|
* @param url 请求下载文件地址
|
||||||
|
* @param timeout 超时时间
|
||||||
|
* @return HttpResponse
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
private static aiyh.utils.tool.cn.hutool.http.HttpResponse requestDownload(String url, int timeout) {
|
||||||
|
Assert.notBlank(url, "[url] is blank !");
|
||||||
|
|
||||||
|
final HttpResponse response = HttpUtil.createGet(url, true)
|
||||||
|
.timeout(timeout)
|
||||||
|
.executeAsync();
|
||||||
|
|
||||||
|
if (response.isOk()) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP异常
|
||||||
|
*
|
||||||
|
* @author xiaoleilu
|
||||||
|
*/
|
||||||
|
public class HttpException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 8247610319171014183L;
|
||||||
|
|
||||||
|
public HttpException(Throwable e) {
|
||||||
|
super(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpException(String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpException(String message, Throwable throwable) {
|
||||||
|
super(message, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, throwable, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpException(Throwable throwable, String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params), throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.RandomUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReflectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.cookie.GlobalCookieManager;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 全局参数配置
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 4.6.2
|
||||||
|
*/
|
||||||
|
public class HttpGlobalConfig implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -1: 含义,永不超时。
|
||||||
|
* 如果:设置timeout = 3s(3000 ms), 那一次请求最大超时:就是:6s
|
||||||
|
* 官方含义:timeout of zero is interpreted as an infinite timeout. (0的超时被解释为无限超时。)
|
||||||
|
* 这里实际项目一定要进行修改,防止把系统拖死.
|
||||||
|
* 底层调用:{@link HttpURLConnection#setReadTimeout(int)} 同时设置: 读取超时
|
||||||
|
* 底层调用:{@link HttpURLConnection#setConnectTimeout(int)} 同时设置: 连接超时
|
||||||
|
*/
|
||||||
|
private static int timeout = -1;
|
||||||
|
private static boolean isAllowPatch = false;
|
||||||
|
private static String boundary = "--------------------Hutool_" + RandomUtil.randomString(16);
|
||||||
|
private static int maxRedirectCount = 0;
|
||||||
|
private static boolean ignoreEOFError = true;
|
||||||
|
private static boolean decodeUrl = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局默认的超时时长
|
||||||
|
*
|
||||||
|
* @return 全局默认的超时时长
|
||||||
|
*/
|
||||||
|
public static int getTimeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认的连接和读取超时时长<br>
|
||||||
|
* -1: 含义,永不超时。<br>
|
||||||
|
* 如果:设置timeout = 3s(3000 ms), 那一次请求最大超时:就是:6s<br>
|
||||||
|
* 官方含义:timeout of zero is interpreted as an infinite timeout. (0的超时被解释为无限超时。)<br>
|
||||||
|
* 这里实际项目一定要进行修改,防止把系统拖死.<br>
|
||||||
|
* 底层调用:{@link HttpURLConnection#setReadTimeout(int)} 同时设置: 读取超时<br>
|
||||||
|
* 底层调用:{@link HttpURLConnection#setConnectTimeout(int)} 同时设置: 连接超时
|
||||||
|
*
|
||||||
|
* @param customTimeout 超时时长
|
||||||
|
*/
|
||||||
|
synchronized public static void setTimeout(int customTimeout) {
|
||||||
|
timeout = customTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局默认的Multipart边界
|
||||||
|
*
|
||||||
|
* @return 全局默认的Multipart边界
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public static String getBoundary() {
|
||||||
|
return boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认的Multipart边界
|
||||||
|
*
|
||||||
|
* @param customBoundary 自定义Multipart边界
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
synchronized public static void setBoundary(String customBoundary) {
|
||||||
|
boundary = customBoundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局默认的最大重定向次数,如设置0表示不重定向<br>
|
||||||
|
* 如果设置为1,表示重定向一次,即请求两次
|
||||||
|
*
|
||||||
|
* @return 全局默认的最大重定向次数
|
||||||
|
* @since 5.7.19
|
||||||
|
*/
|
||||||
|
public static int getMaxRedirectCount() {
|
||||||
|
return maxRedirectCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认全局默认的最大重定向次数,如设置0表示不重定向<br>
|
||||||
|
* 如果设置为1,表示重定向一次,即请求两次
|
||||||
|
*
|
||||||
|
* @param customMaxRedirectCount 全局默认的最大重定向次数
|
||||||
|
* @since 5.7.19
|
||||||
|
*/
|
||||||
|
synchronized public static void setMaxRedirectCount(int customMaxRedirectCount) {
|
||||||
|
maxRedirectCount = customMaxRedirectCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否忽略响应读取时可能的EOF异常。<br>
|
||||||
|
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。<br>
|
||||||
|
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
|
||||||
|
*
|
||||||
|
* @return 是否忽略响应读取时可能的EOF异常
|
||||||
|
* @since 5.7.20
|
||||||
|
*/
|
||||||
|
public static boolean isIgnoreEOFError() {
|
||||||
|
return ignoreEOFError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略响应读取时可能的EOF异常。<br>
|
||||||
|
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。<br>
|
||||||
|
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
|
||||||
|
*
|
||||||
|
* @param customIgnoreEOFError 是否忽略响应读取时可能的EOF异常。
|
||||||
|
* @since 5.7.20
|
||||||
|
*/
|
||||||
|
synchronized public static void setIgnoreEOFError(boolean customIgnoreEOFError) {
|
||||||
|
ignoreEOFError = customIgnoreEOFError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否忽略解码URL,包括URL中的Path部分和Param部分。<br>
|
||||||
|
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果此参数为{@code true},则会统一解码编码后的参数,<br>
|
||||||
|
* 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
|
||||||
|
*
|
||||||
|
* @return 是否忽略解码URL
|
||||||
|
* @since 5.7.22
|
||||||
|
*/
|
||||||
|
public static boolean isDecodeUrl() {
|
||||||
|
return decodeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否忽略解码URL,包括URL中的Path部分和Param部分。<br>
|
||||||
|
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果此参数为{@code true},则会统一解码编码后的参数,<br>
|
||||||
|
* 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
|
||||||
|
*
|
||||||
|
* @param customDecodeUrl 是否忽略解码URL
|
||||||
|
* @since 5.7.22
|
||||||
|
*/
|
||||||
|
synchronized public static void setDecodeUrl(boolean customDecodeUrl) {
|
||||||
|
decodeUrl = customDecodeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Cookie管理器,用于自定义Cookie管理
|
||||||
|
*
|
||||||
|
* @return {@link CookieManager}
|
||||||
|
* @see GlobalCookieManager#getCookieManager()
|
||||||
|
* @since 4.1.0
|
||||||
|
*/
|
||||||
|
public static CookieManager getCookieManager() {
|
||||||
|
return GlobalCookieManager.getCookieManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义{@link CookieManager}
|
||||||
|
*
|
||||||
|
* @param customCookieManager 自定义的{@link CookieManager}
|
||||||
|
* @see GlobalCookieManager#setCookieManager(CookieManager)
|
||||||
|
* @since 4.5.14
|
||||||
|
*/
|
||||||
|
synchronized public static void setCookieManager(CookieManager customCookieManager) {
|
||||||
|
GlobalCookieManager.setCookieManager(customCookieManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭Cookie
|
||||||
|
*
|
||||||
|
* @see GlobalCookieManager#setCookieManager(CookieManager)
|
||||||
|
* @since 4.1.9
|
||||||
|
*/
|
||||||
|
synchronized public static void closeCookie() {
|
||||||
|
GlobalCookieManager.setCookieManager(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加支持的METHOD方法<br>
|
||||||
|
* 此方法通过注入方式修改{@link HttpURLConnection}中的methods静态属性,增加PATCH方法<br>
|
||||||
|
* see: <a href="https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch">https://stackoverflow.com/questions/25163131/httpurlconnection-invalid-http-method-patch</a>
|
||||||
|
*
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
synchronized public static void allowPatch() {
|
||||||
|
if (isAllowPatch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Field methodsField = ReflectUtil.getField(HttpURLConnection.class, "methods");
|
||||||
|
if (null == methodsField) {
|
||||||
|
throw new HttpException("None static field [methods] with Java version: [{}]", System.getProperty("java.version"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除final修饰
|
||||||
|
ReflectUtil.removeFinalModify(methodsField);
|
||||||
|
final String[] methods = {
|
||||||
|
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
|
||||||
|
};
|
||||||
|
ReflectUtil.setFieldValue(null, methodsField, methods);
|
||||||
|
|
||||||
|
// 检查注入是否成功
|
||||||
|
final Object staticFieldValue = ReflectUtil.getStaticFieldValue(methodsField);
|
||||||
|
if (!ArrayUtil.equals(methods, staticFieldValue)) {
|
||||||
|
throw new HttpException("Inject value to field [methods] failed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
isAllowPatch = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP输入流,此流用于包装Http请求响应内容的流,用于解析各种压缩、分段的响应流内容
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HttpInputStream extends InputStream {
|
||||||
|
|
||||||
|
/** 原始流 */
|
||||||
|
private InputStream in;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param response 响应对象
|
||||||
|
*/
|
||||||
|
public HttpInputStream(aiyh.utils.tool.cn.hutool.http.HttpResponse response) {
|
||||||
|
init(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return this.in.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
return this.in.read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
return this.in.skip(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return this.in.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
this.in.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void mark(int readlimit) {
|
||||||
|
this.in.mark(readlimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
this.in.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return this.in.markSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化流
|
||||||
|
*
|
||||||
|
* @param response 响应对象
|
||||||
|
*/
|
||||||
|
private void init(HttpResponse response) {
|
||||||
|
try {
|
||||||
|
this.in = (response.status < HttpStatus.HTTP_BAD_REQUEST) ? response.httpConnection.getInputStream() : response.httpConnection.getErrorStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (false == (e instanceof FileNotFoundException)) {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
// 服务器无返回内容,忽略之
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在一些情况下,返回的流为null,此时提供状态码说明
|
||||||
|
if (null == this.in) {
|
||||||
|
this.in = new ByteArrayInputStream(StrUtil.format("Error request, response status: {}", response.status).getBytes());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.isGzip() && false == (response.in instanceof GZIPInputStream)) {
|
||||||
|
// Accept-Encoding: gzip
|
||||||
|
try {
|
||||||
|
this.in = new GZIPInputStream(this.in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 在类似于Head等方法中无body返回,此时GZIPInputStream构造会出现错误,在此忽略此错误读取普通数据
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else if (response.isDeflate() && false == (this.in instanceof InflaterInputStream)) {
|
||||||
|
// Accept-Encoding: defalte
|
||||||
|
this.in = new InflaterInputStream(this.in, new Inflater(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http拦截器接口,通过实现此接口,完成请求发起前或结束后对请求的编辑工作
|
||||||
|
*
|
||||||
|
* @param <T> 过滤参数类型,HttpRequest或者HttpResponse
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.16
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface HttpInterceptor<T extends aiyh.utils.tool.cn.hutool.http.HttpBase<T>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param httpObj 请求或响应对象
|
||||||
|
*/
|
||||||
|
void process(T httpObj);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截器链
|
||||||
|
*
|
||||||
|
* @param <T> 过滤参数类型,HttpRequest或者HttpResponse
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.16
|
||||||
|
*/
|
||||||
|
class Chain<T extends HttpBase<T>> implements aiyh.utils.tool.cn.hutool.core.lang.Chain<HttpInterceptor<T>, Chain<T>> {
|
||||||
|
private final List<HttpInterceptor<T>> interceptors = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Chain<T> addChain(HttpInterceptor<T> element) {
|
||||||
|
interceptors.add(element);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<HttpInterceptor<T>> iterator() {
|
||||||
|
return interceptors.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public Chain<T> clear() {
|
||||||
|
interceptors.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.resource.Resource;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP资源,可自定义Content-Type
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public class HttpResource implements Resource, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Resource resource;
|
||||||
|
private final String contentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param resource 资源,非空
|
||||||
|
* @param contentType Content-Type类型,{@code null}表示不设置
|
||||||
|
*/
|
||||||
|
public HttpResource(Resource resource, String contentType) {
|
||||||
|
this.resource = Assert.notNull(resource, "Resource must be not null !");
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return resource.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getUrl() {
|
||||||
|
return resource.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getStream() {
|
||||||
|
return resource.getStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取自定义Content-Type类型
|
||||||
|
*
|
||||||
|
* @return Content-Type类型
|
||||||
|
*/
|
||||||
|
public String getContentType() {
|
||||||
|
return this.contentType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,644 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FastByteArrayOutputStream;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FileUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.StreamProgress;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Assert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.URLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.cookie.GlobalCookieManager;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http响应类<br>
|
||||||
|
* 非线程安全对象
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http配置
|
||||||
|
*/
|
||||||
|
protected HttpConfig config;
|
||||||
|
/**
|
||||||
|
* 持有连接对象
|
||||||
|
*/
|
||||||
|
protected HttpConnection httpConnection;
|
||||||
|
/**
|
||||||
|
* Http请求原始流
|
||||||
|
*/
|
||||||
|
protected InputStream in;
|
||||||
|
/**
|
||||||
|
* 是否异步,异步下只持有流,否则将在初始化时直接读取body内容
|
||||||
|
*/
|
||||||
|
private volatile boolean isAsync;
|
||||||
|
/**
|
||||||
|
* 响应状态码
|
||||||
|
*/
|
||||||
|
protected int status;
|
||||||
|
/**
|
||||||
|
* 是否忽略读取Http响应体
|
||||||
|
*/
|
||||||
|
private final boolean ignoreBody;
|
||||||
|
/**
|
||||||
|
* 从响应中获取的编码
|
||||||
|
*/
|
||||||
|
private Charset charsetFromResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param httpConnection {@link HttpConnection}
|
||||||
|
* @param config Http配置
|
||||||
|
* @param charset 编码,从请求编码中获取默认编码
|
||||||
|
* @param isAsync 是否异步
|
||||||
|
* @param isIgnoreBody 是否忽略读取响应体
|
||||||
|
* @since 3.1.2
|
||||||
|
*/
|
||||||
|
protected HttpResponse(HttpConnection httpConnection, HttpConfig config, Charset charset, boolean isAsync, boolean isIgnoreBody) {
|
||||||
|
this.httpConnection = httpConnection;
|
||||||
|
this.config = config;
|
||||||
|
this.charset = charset;
|
||||||
|
this.isAsync = isAsync;
|
||||||
|
this.ignoreBody = isIgnoreBody;
|
||||||
|
initWithDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取状态码
|
||||||
|
*
|
||||||
|
* @return 状态码
|
||||||
|
*/
|
||||||
|
public int getStatus() {
|
||||||
|
return this.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求是否成功,判断依据为:状态码范围在200~299内。
|
||||||
|
*
|
||||||
|
* @return 是否成功请求
|
||||||
|
* @since 4.1.9
|
||||||
|
*/
|
||||||
|
public boolean isOk() {
|
||||||
|
return this.status >= 200 && this.status < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 同步<br>
|
||||||
|
* 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。<br>
|
||||||
|
* 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容中。如果已经是同步状态,则不进行任何操作。
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpResponse sync() {
|
||||||
|
return this.isAsync ? forceSync() : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Http Response Header start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容编码
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String contentEncoding() {
|
||||||
|
return header(aiyh.utils.tool.cn.hutool.http.Header.CONTENT_ENCODING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容长度,以下情况长度无效:
|
||||||
|
* <ul>
|
||||||
|
* <li>Transfer-Encoding: Chunked</li>
|
||||||
|
* <li>Content-Encoding: XXX</li>
|
||||||
|
* </ul>
|
||||||
|
* 参考:https://blog.csdn.net/jiang7701037/article/details/86304302
|
||||||
|
*
|
||||||
|
* @return 长度,-1表示服务端未返回或长度无效
|
||||||
|
* @since 5.7.9
|
||||||
|
*/
|
||||||
|
public long contentLength() {
|
||||||
|
long contentLength = Convert.toLong(header(aiyh.utils.tool.cn.hutool.http.Header.CONTENT_LENGTH), -1L);
|
||||||
|
if (contentLength > 0 && (isChunked() || StrUtil.isNotBlank(contentEncoding()))) {
|
||||||
|
//按照HTTP协议规范,在 Transfer-Encoding和Content-Encoding设置后 Content-Length 无效。
|
||||||
|
contentLength = -1;
|
||||||
|
}
|
||||||
|
return contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为gzip压缩过的内容
|
||||||
|
*
|
||||||
|
* @return 是否为gzip压缩过的内容
|
||||||
|
*/
|
||||||
|
public boolean isGzip() {
|
||||||
|
final String contentEncoding = contentEncoding();
|
||||||
|
return "gzip".equalsIgnoreCase(contentEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为zlib(Deflate)压缩过的内容
|
||||||
|
*
|
||||||
|
* @return 是否为zlib(Deflate)压缩过的内容
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public boolean isDeflate() {
|
||||||
|
final String contentEncoding = contentEncoding();
|
||||||
|
return "deflate".equalsIgnoreCase(contentEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Transfer-Encoding:Chunked的内容
|
||||||
|
*
|
||||||
|
* @return 是否为Transfer-Encoding:Chunked的内容
|
||||||
|
* @since 4.6.2
|
||||||
|
*/
|
||||||
|
public boolean isChunked() {
|
||||||
|
final String transferEncoding = header(aiyh.utils.tool.cn.hutool.http.Header.TRANSFER_ENCODING);
|
||||||
|
return "Chunked".equalsIgnoreCase(transferEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本次请求服务器返回的Cookie信息
|
||||||
|
*
|
||||||
|
* @return Cookie字符串
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
public String getCookieStr() {
|
||||||
|
return header(aiyh.utils.tool.cn.hutool.http.Header.SET_COOKIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Cookie
|
||||||
|
*
|
||||||
|
* @return Cookie列表
|
||||||
|
* @since 3.1.1
|
||||||
|
*/
|
||||||
|
public List<HttpCookie> getCookies() {
|
||||||
|
return GlobalCookieManager.getCookies(this.httpConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Cookie
|
||||||
|
*
|
||||||
|
* @param name Cookie名
|
||||||
|
* @return {@link HttpCookie}
|
||||||
|
* @since 4.1.4
|
||||||
|
*/
|
||||||
|
public HttpCookie getCookie(String name) {
|
||||||
|
List<HttpCookie> cookie = getCookies();
|
||||||
|
if (null != cookie) {
|
||||||
|
for (HttpCookie httpCookie : cookie) {
|
||||||
|
if (httpCookie.getName().equals(name)) {
|
||||||
|
return httpCookie;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Cookie值
|
||||||
|
*
|
||||||
|
* @param name Cookie名
|
||||||
|
* @return Cookie值
|
||||||
|
* @since 4.1.4
|
||||||
|
*/
|
||||||
|
public String getCookieValue(String name) {
|
||||||
|
HttpCookie cookie = getCookie(name);
|
||||||
|
return (null == cookie) ? null : cookie.getValue();
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------- Http Response Header end
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Body start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得服务区响应流<br>
|
||||||
|
* 异步模式下获取Http原生流,同步模式下获取获取到的在内存中的副本<br>
|
||||||
|
* 如果想在同步模式下获取流,请先调用{@link #sync()}方法强制同步<br>
|
||||||
|
* 流获取后处理完毕需关闭此类
|
||||||
|
*
|
||||||
|
* @return 响应流
|
||||||
|
*/
|
||||||
|
public InputStream bodyStream() {
|
||||||
|
if (isAsync) {
|
||||||
|
return this.in;
|
||||||
|
}
|
||||||
|
return new ByteArrayInputStream(this.bodyBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应流字节码<br>
|
||||||
|
* 此方法会转为同步模式
|
||||||
|
*
|
||||||
|
* @return byte[]
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public byte[] bodyBytes() {
|
||||||
|
sync();
|
||||||
|
return this.bodyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置主体字节码<br>
|
||||||
|
* 需在此方法调用前使用charset方法设置编码,否则使用默认编码UTF-8
|
||||||
|
*
|
||||||
|
* @param bodyBytes 主体
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpResponse body(byte[] bodyBytes) {
|
||||||
|
sync();
|
||||||
|
if (null != bodyBytes) {
|
||||||
|
this.bodyBytes = bodyBytes;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应主体
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
* @throws HttpException 包装IO异常
|
||||||
|
*/
|
||||||
|
public String body() throws HttpException {
|
||||||
|
return HttpUtil.getString(bodyBytes(), this.charset, null == this.charsetFromResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到{@link OutputStream}<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param out 写出的流
|
||||||
|
* @param isCloseOut 是否关闭输出流
|
||||||
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
||||||
|
* @return 写出bytes数
|
||||||
|
* @since 3.3.2
|
||||||
|
*/
|
||||||
|
public long writeBody(OutputStream out, boolean isCloseOut, StreamProgress streamProgress) {
|
||||||
|
Assert.notNull(out, "[out] must be not null!");
|
||||||
|
final long contentLength = contentLength();
|
||||||
|
try {
|
||||||
|
return copyBody(bodyStream(), out, contentLength, streamProgress, this.config.ignoreEOFError);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(this);
|
||||||
|
if (isCloseOut) {
|
||||||
|
IoUtil.close(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到文件<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 写出到的文件或目录
|
||||||
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
||||||
|
* @return 写出bytes数
|
||||||
|
* @since 3.3.2
|
||||||
|
*/
|
||||||
|
public long writeBody(File targetFileOrDir, StreamProgress streamProgress) {
|
||||||
|
Assert.notNull(targetFileOrDir, "[targetFileOrDir] must be not null!");
|
||||||
|
|
||||||
|
final File outFile = completeFileNameFromHeader(targetFileOrDir);
|
||||||
|
return writeBody(FileUtil.getOutputStream(outFile), true, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到文件-避免未完成的文件<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)<br>
|
||||||
|
* 来自:https://gitee.com/dromara/hutool/pulls/407<br>
|
||||||
|
* 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 写出到的文件或目录
|
||||||
|
* @param tempFileSuffix 临时文件后缀,默认".temp"
|
||||||
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
||||||
|
* @return 写出bytes数
|
||||||
|
* @since 5.7.12
|
||||||
|
*/
|
||||||
|
public long writeBody(File targetFileOrDir, String tempFileSuffix, StreamProgress streamProgress) {
|
||||||
|
Assert.notNull(targetFileOrDir, "[targetFileOrDir] must be not null!");
|
||||||
|
|
||||||
|
File outFile = completeFileNameFromHeader(targetFileOrDir);
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(tempFileSuffix)) {
|
||||||
|
tempFileSuffix = ".temp";
|
||||||
|
} else {
|
||||||
|
tempFileSuffix = StrUtil.addPrefixIfNot(tempFileSuffix, StrUtil.DOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 目标文件真实名称
|
||||||
|
final String fileName = outFile.getName();
|
||||||
|
// 临时文件名称
|
||||||
|
final String tempFileName = fileName + tempFileSuffix;
|
||||||
|
|
||||||
|
// 临时文件
|
||||||
|
outFile = new File(outFile.getParentFile(), tempFileName);
|
||||||
|
|
||||||
|
long length;
|
||||||
|
try {
|
||||||
|
length = writeBody(outFile, streamProgress);
|
||||||
|
// 重命名下载好的临时文件
|
||||||
|
FileUtil.rename(outFile, fileName, true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// 异常则删除临时文件
|
||||||
|
FileUtil.del(outFile);
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到文件<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 写出到的文件
|
||||||
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
||||||
|
* @return 写出的文件
|
||||||
|
* @since 5.6.4
|
||||||
|
*/
|
||||||
|
public File writeBodyForFile(File targetFileOrDir, StreamProgress streamProgress) {
|
||||||
|
Assert.notNull(targetFileOrDir, "[targetFileOrDir] must be not null!");
|
||||||
|
|
||||||
|
final File outFile = completeFileNameFromHeader(targetFileOrDir);
|
||||||
|
writeBody(FileUtil.getOutputStream(outFile), true, streamProgress);
|
||||||
|
|
||||||
|
return outFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到文件<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 写出到的文件或目录
|
||||||
|
* @return 写出bytes数
|
||||||
|
* @since 3.3.2
|
||||||
|
*/
|
||||||
|
public long writeBody(File targetFileOrDir) {
|
||||||
|
return writeBody(targetFileOrDir, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到文件<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 写出到的文件或目录的路径
|
||||||
|
* @return 写出bytes数
|
||||||
|
* @since 3.3.2
|
||||||
|
*/
|
||||||
|
public long writeBody(String targetFileOrDir) {
|
||||||
|
return writeBody(FileUtil.file(targetFileOrDir));
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------- Body end
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
IoUtil.close(this.in);
|
||||||
|
this.in = null;
|
||||||
|
// 关闭连接
|
||||||
|
this.httpConnection.disconnectQuietly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = StrUtil.builder();
|
||||||
|
sb.append("Response Headers: ").append(StrUtil.CRLF);
|
||||||
|
for (Entry<String, List<String>> entry : this.headers.entrySet()) {
|
||||||
|
sb.append(" ").append(entry).append(StrUtil.CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("Response Body: ").append(StrUtil.CRLF);
|
||||||
|
sb.append(" ").append(this.body()).append(StrUtil.CRLF);
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从响应头补全下载文件名
|
||||||
|
*
|
||||||
|
* @param targetFileOrDir 目标文件夹或者目标文件
|
||||||
|
* @return File 保存的文件
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public File completeFileNameFromHeader(File targetFileOrDir) {
|
||||||
|
if (false == targetFileOrDir.isDirectory()) {
|
||||||
|
// 非目录直接返回
|
||||||
|
return targetFileOrDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从头信息中获取文件名
|
||||||
|
String fileName = getFileNameFromDisposition(null);
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
final String path = httpConnection.getUrl().getPath();
|
||||||
|
// 从路径中获取文件名
|
||||||
|
fileName = StrUtil.subSuf(path, path.lastIndexOf('/') + 1);
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
// 编码后的路径做为文件名
|
||||||
|
fileName = URLUtil.encodeQuery(path, charset);
|
||||||
|
} else {
|
||||||
|
// issue#I4K0FS@Gitee
|
||||||
|
fileName = URLUtil.decode(fileName, charset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FileUtil.file(targetFileOrDir, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Content-Disposition头中获取文件名
|
||||||
|
* @param paramName 文件参数名
|
||||||
|
*
|
||||||
|
* @return 文件名,empty表示无
|
||||||
|
*/
|
||||||
|
public String getFileNameFromDisposition(String paramName) {
|
||||||
|
paramName = ObjUtil.defaultIfNull(paramName, "filename");
|
||||||
|
String fileName = null;
|
||||||
|
final String disposition = header(Header.CONTENT_DISPOSITION);
|
||||||
|
if (StrUtil.isNotBlank(disposition)) {
|
||||||
|
fileName = ReUtil.get(paramName+"=\"(.*?)\"", disposition, 1);
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
fileName = StrUtil.subAfter(disposition, paramName + "=", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Http响应,并在报错时关闭连接。<br>
|
||||||
|
* 初始化包括:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、读取Http状态
|
||||||
|
* 2、读取头信息
|
||||||
|
* 3、持有Http流,并不关闭流
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @throws HttpException IO异常
|
||||||
|
*/
|
||||||
|
private HttpResponse initWithDisconnect() throws HttpException {
|
||||||
|
try {
|
||||||
|
init();
|
||||||
|
} catch (HttpException e) {
|
||||||
|
this.httpConnection.disconnectQuietly();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Http响应<br>
|
||||||
|
* 初始化包括:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、读取Http状态
|
||||||
|
* 2、读取头信息
|
||||||
|
* 3、持有Http流,并不关闭流
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @throws HttpException IO异常
|
||||||
|
*/
|
||||||
|
private HttpResponse init() throws HttpException {
|
||||||
|
// 获取响应状态码
|
||||||
|
try {
|
||||||
|
this.status = httpConnection.responseCode();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (false == (e instanceof FileNotFoundException)) {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
// 服务器无返回内容,忽略之
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 读取响应头信息
|
||||||
|
try {
|
||||||
|
this.headers = httpConnection.headers();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// ignore
|
||||||
|
// StaticLog.warn(e, e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储服务端设置的Cookie信息
|
||||||
|
GlobalCookieManager.store(httpConnection);
|
||||||
|
|
||||||
|
// 获取响应编码
|
||||||
|
final Charset charset = httpConnection.getCharset();
|
||||||
|
this.charsetFromResponse = charset;
|
||||||
|
if (null != charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取响应内容流
|
||||||
|
this.in = new HttpInputStream(this);
|
||||||
|
|
||||||
|
// 同步情况下强制同步
|
||||||
|
return this.isAsync ? this : forceSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制同步,用于初始化<br>
|
||||||
|
* 强制同步后变化如下:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、读取body内容到内存
|
||||||
|
* 2、异步状态设为false(变为同步状态)
|
||||||
|
* 3、关闭Http流
|
||||||
|
* 4、断开与服务器连接
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
private HttpResponse forceSync() {
|
||||||
|
// 非同步状态转为同步状态
|
||||||
|
try {
|
||||||
|
this.readBody(this.in);
|
||||||
|
} catch (IORuntimeException e) {
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if (e.getCause() instanceof FileNotFoundException) {
|
||||||
|
// 服务器无返回内容,忽略之
|
||||||
|
} else {
|
||||||
|
throw new HttpException(e);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (this.isAsync) {
|
||||||
|
this.isAsync = false;
|
||||||
|
}
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取主体,忽略EOFException异常
|
||||||
|
*
|
||||||
|
* @param in 输入流
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
private void readBody(InputStream in) throws IORuntimeException {
|
||||||
|
if (ignoreBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final long contentLength = contentLength();
|
||||||
|
final FastByteArrayOutputStream out = new FastByteArrayOutputStream((int) contentLength);
|
||||||
|
copyBody(in, out, contentLength, null, this.config.ignoreEOFError);
|
||||||
|
this.bodyBytes = out.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将响应内容写出到{@link OutputStream}<br>
|
||||||
|
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出<br>
|
||||||
|
* 写出后会关闭Http流(异步模式)
|
||||||
|
*
|
||||||
|
* @param in 输入流
|
||||||
|
* @param out 写出的流
|
||||||
|
* @param contentLength 总长度,-1表示未知
|
||||||
|
* @param streamProgress 进度显示接口,通过实现此接口显示下载进度
|
||||||
|
* @param isIgnoreEOFError 是否忽略响应读取时可能的EOF异常
|
||||||
|
* @return 拷贝长度
|
||||||
|
*/
|
||||||
|
private static long copyBody(InputStream in, OutputStream out, long contentLength, StreamProgress streamProgress, boolean isIgnoreEOFError) {
|
||||||
|
if (null == out) {
|
||||||
|
throw new NullPointerException("[out] is null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
long copyLength = -1;
|
||||||
|
try {
|
||||||
|
copyLength = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE, contentLength, streamProgress);
|
||||||
|
} catch (IORuntimeException e) {
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if (isIgnoreEOFError
|
||||||
|
&& (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF"))) {
|
||||||
|
// 忽略读取HTTP流中的EOF错误
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copyLength;
|
||||||
|
}
|
||||||
|
// ---------------------------------------------------------------- Private method end
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP状态码
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @see java.net.HttpURLConnection
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HttpStatus {
|
||||||
|
|
||||||
|
/* 2XX: generally "OK" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 200: OK.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_OK = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 201: Created.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_CREATED = 201;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 202: Accepted.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_ACCEPTED = 202;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 203: Non-Authoritative Information.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NOT_AUTHORITATIVE = 203;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 204: No Content.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NO_CONTENT = 204;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 205: Reset Content.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_RESET = 205;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 206: Partial Content.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_PARTIAL = 206;
|
||||||
|
|
||||||
|
/* 3XX: relocation/redirect */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 300: Multiple Choices.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_MULT_CHOICE = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 301: Moved Permanently.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_MOVED_PERM = 301;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 302: Temporary Redirect.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_MOVED_TEMP = 302;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 303: See Other.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_SEE_OTHER = 303;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 304: Not Modified.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NOT_MODIFIED = 304;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 305: Use Proxy.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_USE_PROXY = 305;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 1.1 Status-Code 307: Temporary Redirect.<br>
|
||||||
|
* 见:RFC-7231
|
||||||
|
*/
|
||||||
|
public static final int HTTP_TEMP_REDIRECT = 307;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 1.1 Status-Code 308: Permanent Redirect 永久重定向<br>
|
||||||
|
* 见:RFC-7231
|
||||||
|
*/
|
||||||
|
public static final int HTTP_PERMANENT_REDIRECT = 308;
|
||||||
|
|
||||||
|
/* 4XX: client error */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 400: Bad Request.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_BAD_REQUEST = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 401: Unauthorized.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_UNAUTHORIZED = 401;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 402: Payment Required.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_PAYMENT_REQUIRED = 402;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 403: Forbidden.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_FORBIDDEN = 403;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 404: Not Found.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NOT_FOUND = 404;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 405: Method Not Allowed.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_BAD_METHOD = 405;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 406: Not Acceptable.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NOT_ACCEPTABLE = 406;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 407: Proxy Authentication Required.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_PROXY_AUTH = 407;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 408: Request Time-Out.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_CLIENT_TIMEOUT = 408;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 409: Conflict.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_CONFLICT = 409;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 410: Gone.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_GONE = 410;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 411: Length Required.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_LENGTH_REQUIRED = 411;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 412: Precondition Failed.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_PRECON_FAILED = 412;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 413: Request Entity Too Large.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_ENTITY_TOO_LARGE = 413;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 414: Request-URI Too Large.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_REQ_TOO_LONG = 414;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 415: Unsupported Media Type.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_UNSUPPORTED_TYPE = 415;
|
||||||
|
|
||||||
|
/* 5XX: server error */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 500: Internal Server Error.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_INTERNAL_ERROR = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 501: Not Implemented.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_NOT_IMPLEMENTED = 501;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 502: Bad Gateway.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_BAD_GATEWAY = 502;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 503: Service Unavailable.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_UNAVAILABLE = 503;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 504: Gateway Timeout.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_GATEWAY_TIMEOUT = 504;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 505: HTTP Version Not Supported.
|
||||||
|
*/
|
||||||
|
public static final int HTTP_VERSION = 505;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为重定向状态码
|
||||||
|
* @param responseCode 被检查的状态码
|
||||||
|
* @return 是否为重定向状态码
|
||||||
|
* @since 5.6.3
|
||||||
|
*/
|
||||||
|
public static boolean isRedirected(int responseCode){
|
||||||
|
return responseCode == HTTP_MOVED_PERM
|
||||||
|
|| responseCode == HTTP_MOVED_TEMP
|
||||||
|
|| responseCode == HTTP_SEE_OTHER
|
||||||
|
// issue#1504@Github,307和308是RFC 7538中http 1.1定义的规范
|
||||||
|
|| responseCode == HTTP_TEMP_REDIRECT
|
||||||
|
|| responseCode == HTTP_PERMANENT_REDIRECT;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,893 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.codec.Base64;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FileUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.StreamProgress;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.RFC3986;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.url.UrlQuery;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.text.StrBuilder;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharsetUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.URLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.cookie.GlobalCookieManager;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.SimpleServer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http请求工具类
|
||||||
|
*
|
||||||
|
* @author xiaoleilu
|
||||||
|
*/
|
||||||
|
public class HttpUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则:Content-Type中的编码信息
|
||||||
|
*/
|
||||||
|
public static final Pattern CHARSET_PATTERN = Pattern.compile("charset\\s*=\\s*([a-z0-9-]*)", Pattern.CASE_INSENSITIVE);
|
||||||
|
/**
|
||||||
|
* 正则:匹配meta标签的编码信息
|
||||||
|
*/
|
||||||
|
public static final Pattern META_CHARSET_PATTERN = Pattern.compile("<meta[^>]*?charset\\s*=\\s*['\"]?([a-z0-9-]*)", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否https
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @return 是否https
|
||||||
|
*/
|
||||||
|
public static boolean isHttps(String url) {
|
||||||
|
return StrUtil.startWithIgnoreCase(url, "https:");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测是否http
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @return 是否http
|
||||||
|
* @since 5.3.8
|
||||||
|
*/
|
||||||
|
public static boolean isHttp(String url) {
|
||||||
|
return StrUtil.startWithIgnoreCase(url, "http:");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Http请求对象
|
||||||
|
*
|
||||||
|
* @param method 方法枚举{@link aiyh.utils.tool.cn.hutool.http.Method}
|
||||||
|
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.HttpRequest}
|
||||||
|
* @since 3.0.9
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.HttpRequest createRequest(Method method, String url) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.of(url).method(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Http GET请求对象
|
||||||
|
*
|
||||||
|
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.HttpRequest}
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.HttpRequest createGet(String url) {
|
||||||
|
return createGet(url, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Http GET请求对象
|
||||||
|
*
|
||||||
|
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||||
|
* @param isFollowRedirects 是否打开重定向
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.HttpRequest}
|
||||||
|
* @since 5.6.4
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.HttpRequest createGet(String url, boolean isFollowRedirects) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建Http POST请求对象
|
||||||
|
*
|
||||||
|
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.HttpRequest}
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.HttpRequest createPost(String url) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.post(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送get请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param customCharset 自定义请求字符集,如果字符集获取不到,使用此字符集
|
||||||
|
* @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null
|
||||||
|
*/
|
||||||
|
public static String get(String urlString, Charset customCharset) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.get(urlString).charset(customCharset).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送get请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null
|
||||||
|
*/
|
||||||
|
public static String get(String urlString) {
|
||||||
|
return get(urlString, HttpGlobalConfig.getTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送get请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||||
|
* @return 返回内容,如果只检查状态码,正常只返回 "",不正常返回 null
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static String get(String urlString, int timeout) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.get(urlString).timeout(timeout).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送get请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param paramMap post表单数据
|
||||||
|
* @return 返回数据
|
||||||
|
*/
|
||||||
|
public static String get(String urlString, Map<String, Object> paramMap) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.get(urlString).form(paramMap).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送get请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param paramMap post表单数据
|
||||||
|
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||||
|
* @return 返回数据
|
||||||
|
* @since 3.3.0
|
||||||
|
*/
|
||||||
|
public static String get(String urlString, Map<String, Object> paramMap, int timeout) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.get(urlString).form(paramMap).timeout(timeout).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送post请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param paramMap post表单数据
|
||||||
|
* @return 返回数据
|
||||||
|
*/
|
||||||
|
public static String post(String urlString, Map<String, Object> paramMap) {
|
||||||
|
return post(urlString, paramMap, HttpGlobalConfig.getTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送post请求
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param paramMap post表单数据
|
||||||
|
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||||
|
* @return 返回数据
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static String post(String urlString, Map<String, Object> paramMap, int timeout) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.HttpRequest.post(urlString).form(paramMap).timeout(timeout).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送post请求<br>
|
||||||
|
* 请求体body参数支持两种类型:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 标准参数,例如 a=1&b=2 这种格式
|
||||||
|
* 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param body post表单数据
|
||||||
|
* @return 返回数据
|
||||||
|
*/
|
||||||
|
public static String post(String urlString, String body) {
|
||||||
|
return post(urlString, body, HttpGlobalConfig.getTimeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送post请求<br>
|
||||||
|
* 请求体body参数支持两种类型:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 标准参数,例如 a=1&b=2 这种格式
|
||||||
|
* 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param urlString 网址
|
||||||
|
* @param body post表单数据
|
||||||
|
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||||
|
* @return 返回数据
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static String post(String urlString, String body, int timeout) {
|
||||||
|
return HttpRequest.post(urlString).timeout(timeout).body(body).execute().body();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------- download
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文本
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param customCharsetName 自定义的字符集
|
||||||
|
* @return 文本
|
||||||
|
*/
|
||||||
|
public static String downloadString(String url, String customCharsetName) {
|
||||||
|
return downloadString(url, CharsetUtil.charset(customCharsetName), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文本
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
|
||||||
|
* @return 文本
|
||||||
|
*/
|
||||||
|
public static String downloadString(String url, Charset customCharset) {
|
||||||
|
return downloadString(url, customCharset, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文本
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
|
||||||
|
* @param streamPress 进度条 {@link StreamProgress}
|
||||||
|
* @return 文本
|
||||||
|
*/
|
||||||
|
public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) {
|
||||||
|
return HttpDownloader.downloadString(url, customCharset, streamPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, String dest) {
|
||||||
|
return downloadFile(url, FileUtil.file(dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, File destFile) {
|
||||||
|
return downloadFile(url, destFile, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @return 文件大小
|
||||||
|
* @since 4.0.4
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, File destFile, int timeout) {
|
||||||
|
return downloadFile(url, destFile, timeout, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, File destFile, StreamProgress streamProgress) {
|
||||||
|
return downloadFile(url, destFile, -1, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件大小
|
||||||
|
* @since 4.0.4
|
||||||
|
*/
|
||||||
|
public static long downloadFile(String url, File destFile, int timeout, StreamProgress streamProgress) {
|
||||||
|
return HttpDownloader.downloadFile(url, destFile, timeout, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @return 下载的文件对象
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public static File downloadFileFromUrl(String url, String dest) {
|
||||||
|
return downloadFileFromUrl(url, FileUtil.file(dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @return 下载的文件对象
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public static File downloadFileFromUrl(String url, File destFile) {
|
||||||
|
return downloadFileFromUrl(url, destFile, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @return 下载的文件对象
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public static File downloadFileFromUrl(String url, File destFile, int timeout) {
|
||||||
|
return downloadFileFromUrl(url, destFile, timeout, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 下载的文件对象
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public static File downloadFileFromUrl(String url, File destFile, StreamProgress streamProgress) {
|
||||||
|
return downloadFileFromUrl(url, destFile, -1, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||||
|
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 下载的文件对象
|
||||||
|
* @since 5.4.1
|
||||||
|
*/
|
||||||
|
public static File downloadFileFromUrl(String url, File destFile, int timeout, StreamProgress streamProgress) {
|
||||||
|
return HttpDownloader.downloadForFile(url, destFile, timeout, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||||
|
* @param isCloseOut 是否关闭输出流
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long download(String url, OutputStream out, boolean isCloseOut) {
|
||||||
|
return download(url, out, isCloseOut, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||||
|
* @param isCloseOut 是否关闭输出流
|
||||||
|
* @param streamProgress 进度条
|
||||||
|
* @return 文件大小
|
||||||
|
*/
|
||||||
|
public static long download(String url, OutputStream out, boolean isCloseOut, StreamProgress streamProgress) {
|
||||||
|
return HttpDownloader.download(url, out, isCloseOut, streamProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载远程文件数据,支持30x跳转
|
||||||
|
*
|
||||||
|
* @param url 请求的url
|
||||||
|
* @return 文件数据
|
||||||
|
* @since 5.3.6
|
||||||
|
*/
|
||||||
|
public static byte[] downloadBytes(String url) {
|
||||||
|
return HttpDownloader.downloadBytes(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map形式的Form表单数据转换为Url参数形式,会自动url编码键和值
|
||||||
|
*
|
||||||
|
* @param paramMap 表单数据
|
||||||
|
* @return url参数
|
||||||
|
*/
|
||||||
|
public static String toParams(Map<String, ?> paramMap) {
|
||||||
|
return toParams(paramMap, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||||
|
* 编码键和值对
|
||||||
|
*
|
||||||
|
* @param paramMap 表单数据
|
||||||
|
* @param charsetName 编码
|
||||||
|
* @return url参数
|
||||||
|
* @deprecated 请使用 {@link #toParams(Map, Charset)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static String toParams(Map<String, Object> paramMap, String charsetName) {
|
||||||
|
return toParams(paramMap, CharsetUtil.charset(charsetName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||||
|
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||||
|
* 会自动url编码键和值<br>
|
||||||
|
* 此方法用于拼接URL中的Query部分,并不适用于POST请求中的表单
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* key1=v1&key2=&key3=v3
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param paramMap 表单数据
|
||||||
|
* @param charset 编码,{@code null} 表示不encode键值对
|
||||||
|
* @return url参数
|
||||||
|
* @see #toParams(Map, Charset, boolean)
|
||||||
|
*/
|
||||||
|
public static String toParams(Map<String, ?> paramMap, Charset charset) {
|
||||||
|
return toParams(paramMap, charset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||||
|
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||||
|
* 会自动url编码键和值
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* key1=v1&key2=&key3=v3
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param paramMap 表单数据
|
||||||
|
* @param charset 编码,null表示不encode键值对
|
||||||
|
* @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
|
||||||
|
* @return url参数
|
||||||
|
* @since 5.7.16
|
||||||
|
*/
|
||||||
|
public static String toParams(Map<String, ?> paramMap, Charset charset, boolean isFormUrlEncoded) {
|
||||||
|
return UrlQuery.of(paramMap, isFormUrlEncoded).build(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对URL参数做编码,只编码键和值<br>
|
||||||
|
* 提供的值可以是url附带参数,但是不能只是url
|
||||||
|
*
|
||||||
|
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||||
|
*
|
||||||
|
* @param urlWithParams url和参数,可以包含url本身,也可以单独参数
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 编码后的url和参数
|
||||||
|
* @since 4.0.1
|
||||||
|
*/
|
||||||
|
public static String encodeParams(String urlWithParams, Charset charset) {
|
||||||
|
if (StrUtil.isBlank(urlWithParams)) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
String urlPart = null; // url部分,不包括问号
|
||||||
|
String paramPart; // 参数部分
|
||||||
|
final int pathEndPos = urlWithParams.indexOf('?');
|
||||||
|
if (pathEndPos > -1) {
|
||||||
|
// url + 参数
|
||||||
|
urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
|
||||||
|
paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
|
||||||
|
if (StrUtil.isBlank(paramPart)) {
|
||||||
|
// 无参数,返回url
|
||||||
|
return urlPart;
|
||||||
|
}
|
||||||
|
} else if (false == StrUtil.contains(urlWithParams, '=')) {
|
||||||
|
// 无参数的URL
|
||||||
|
return urlWithParams;
|
||||||
|
} else {
|
||||||
|
// 无URL的参数
|
||||||
|
paramPart = urlWithParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
paramPart = normalizeParams(paramPart, charset);
|
||||||
|
|
||||||
|
return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准化参数字符串,即URL中?后的部分
|
||||||
|
*
|
||||||
|
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||||
|
*
|
||||||
|
* @param paramPart 参数字符串
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 标准化的参数字符串
|
||||||
|
* @since 4.5.2
|
||||||
|
*/
|
||||||
|
public static String normalizeParams(String paramPart, Charset charset) {
|
||||||
|
if(StrUtil.isEmpty(paramPart)){
|
||||||
|
return paramPart;
|
||||||
|
}
|
||||||
|
final StrBuilder builder = StrBuilder.create(paramPart.length() + 16);
|
||||||
|
final int len = paramPart.length();
|
||||||
|
String name = null;
|
||||||
|
int pos = 0; // 未处理字符开始位置
|
||||||
|
char c; // 当前字符
|
||||||
|
int i; // 当前字符位置
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
c = paramPart.charAt(i);
|
||||||
|
if (c == '=') { // 键值对的分界点
|
||||||
|
if (null == name) {
|
||||||
|
// 只有=前未定义name时被当作键值分界符,否则做为普通字符
|
||||||
|
name = (pos == i) ? StrUtil.EMPTY : paramPart.substring(pos, i);
|
||||||
|
pos = i + 1;
|
||||||
|
}
|
||||||
|
} else if (c == '&') { // 参数对的分界点
|
||||||
|
if (pos != i) {
|
||||||
|
if (null == name) {
|
||||||
|
// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
|
||||||
|
name = paramPart.substring(pos, i);
|
||||||
|
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=');
|
||||||
|
} else {
|
||||||
|
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=')
|
||||||
|
.append(RFC3986.QUERY_PARAM_VALUE.encode(paramPart.substring(pos, i), charset)).append('&');
|
||||||
|
}
|
||||||
|
name = null;
|
||||||
|
}
|
||||||
|
pos = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结尾处理
|
||||||
|
if (null != name) {
|
||||||
|
builder.append(URLUtil.encodeQuery(name, charset)).append('=');
|
||||||
|
}
|
||||||
|
if (pos != i) {
|
||||||
|
if (null == name && pos > 0) {
|
||||||
|
builder.append('=');
|
||||||
|
}
|
||||||
|
builder.append(URLUtil.encodeQuery(paramPart.substring(pos, i), charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以&结尾则去除之
|
||||||
|
int lastIndex = builder.length() - 1;
|
||||||
|
if ('&' == builder.charAt(lastIndex)) {
|
||||||
|
builder.delTo(lastIndex);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||||
|
*
|
||||||
|
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return 参数Map
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public static Map<String, String> decodeParamMap(String paramsStr, Charset charset) {
|
||||||
|
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||||
|
if (MapUtil.isEmpty(queryMap)) {
|
||||||
|
return MapUtil.empty();
|
||||||
|
}
|
||||||
|
return Convert.toMap(String.class, String.class, queryMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||||
|
*
|
||||||
|
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return 参数Map
|
||||||
|
*/
|
||||||
|
public static Map<String, List<String>> decodeParams(String paramsStr, String charset) {
|
||||||
|
return decodeParams(paramsStr, charset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||||
|
*
|
||||||
|
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||||
|
* @param charset 字符集
|
||||||
|
* @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
|
||||||
|
* @return 参数Map
|
||||||
|
* @since 5.8.12
|
||||||
|
*/
|
||||||
|
public static Map<String, List<String>> decodeParams(String paramsStr, String charset, boolean isFormUrlEncoded) {
|
||||||
|
return decodeParams(paramsStr, CharsetUtil.charset(charset), isFormUrlEncoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URL QueryString参数解析为Map
|
||||||
|
*
|
||||||
|
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return 参数Map
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public static Map<String, List<String>> decodeParams(String paramsStr, Charset charset) {
|
||||||
|
return decodeParams(paramsStr, charset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||||
|
*
|
||||||
|
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||||
|
* @param charset 字符集
|
||||||
|
* @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
|
||||||
|
* @return 参数Map
|
||||||
|
*/
|
||||||
|
public static Map<String, List<String>> decodeParams(String paramsStr, Charset charset, boolean isFormUrlEncoded) {
|
||||||
|
final Map<CharSequence, CharSequence> queryMap =
|
||||||
|
UrlQuery.of(paramsStr, charset, true, isFormUrlEncoded).getQueryMap();
|
||||||
|
if (MapUtil.isEmpty(queryMap)) {
|
||||||
|
return MapUtil.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, List<String>> params = new LinkedHashMap<>();
|
||||||
|
queryMap.forEach((key, value) -> {
|
||||||
|
final List<String> values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
|
||||||
|
// 一般是一个参数
|
||||||
|
values.add(StrUtil.str(value));
|
||||||
|
});
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将表单数据加到URL中(用于GET表单提交)<br>
|
||||||
|
* 表单的键值对会被url编码,但是url中原参数不会被编码
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param form 表单数据
|
||||||
|
* @param charset 编码
|
||||||
|
* @param isEncodeParams 是否对键和值做转义处理
|
||||||
|
* @return 合成后的URL
|
||||||
|
*/
|
||||||
|
public static String urlWithForm(String url, Map<String, Object> form, Charset charset, boolean isEncodeParams) {
|
||||||
|
if (isEncodeParams && StrUtil.contains(url, '?')) {
|
||||||
|
// 在需要编码的情况下,如果url中已经有部分参数,则编码之
|
||||||
|
url = encodeParams(url, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// url和参数是分别编码的
|
||||||
|
return urlWithForm(url, toParams(form, charset), charset, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将表单数据字符串加到URL中(用于GET表单提交)
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @param queryString 表单数据字符串
|
||||||
|
* @param charset 编码
|
||||||
|
* @param isEncode 是否对键和值做转义处理
|
||||||
|
* @return 拼接后的字符串
|
||||||
|
*/
|
||||||
|
public static String urlWithForm(String url, String queryString, Charset charset, boolean isEncode) {
|
||||||
|
if (StrUtil.isBlank(queryString)) {
|
||||||
|
// 无额外参数
|
||||||
|
if (StrUtil.contains(url, '?')) {
|
||||||
|
// url中包含参数
|
||||||
|
return isEncode ? encodeParams(url, charset) : url;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 始终有参数
|
||||||
|
final StrBuilder urlBuilder = StrBuilder.create(url.length() + queryString.length() + 16);
|
||||||
|
int qmIndex = url.indexOf('?');
|
||||||
|
if (qmIndex > 0) {
|
||||||
|
// 原URL带参数,则对这部分参数单独编码(如果选项为进行编码)
|
||||||
|
urlBuilder.append(isEncode ? encodeParams(url, charset) : url);
|
||||||
|
if (false == StrUtil.endWith(url, '&')) {
|
||||||
|
// 已经带参数的情况下追加参数
|
||||||
|
urlBuilder.append('&');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 原url无参数,则不做编码
|
||||||
|
urlBuilder.append(url);
|
||||||
|
if (qmIndex < 0) {
|
||||||
|
// 无 '?' 追加之
|
||||||
|
urlBuilder.append('?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urlBuilder.append(isEncode ? encodeParams(queryString, charset) : queryString);
|
||||||
|
return urlBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Http连接的头信息中获得字符集<br>
|
||||||
|
* 从ContentType中获取
|
||||||
|
*
|
||||||
|
* @param conn HTTP连接对象
|
||||||
|
* @return 字符集
|
||||||
|
*/
|
||||||
|
public static String getCharset(HttpURLConnection conn) {
|
||||||
|
if (conn == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getCharset(conn.getContentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Http连接的头信息中获得字符集<br>
|
||||||
|
* 从ContentType中获取
|
||||||
|
*
|
||||||
|
* @param contentType Content-Type
|
||||||
|
* @return 字符集
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public static String getCharset(String contentType) {
|
||||||
|
if (StrUtil.isBlank(contentType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReUtil.get(CHARSET_PATTERN, contentType, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从流中读取内容<br>
|
||||||
|
* 首先尝试使用charset编码读取内容(如果为空默认UTF-8),如果isGetCharsetFromContent为true,则通过正则在正文中获取编码信息,转换为指定编码;
|
||||||
|
*
|
||||||
|
* @param in 输入流
|
||||||
|
* @param charset 字符集
|
||||||
|
* @param isGetCharsetFromContent 是否从返回内容中获得编码信息
|
||||||
|
* @return 内容
|
||||||
|
*/
|
||||||
|
public static String getString(InputStream in, Charset charset, boolean isGetCharsetFromContent) {
|
||||||
|
final byte[] contentBytes = IoUtil.readBytes(in);
|
||||||
|
return getString(contentBytes, charset, isGetCharsetFromContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从流中读取内容<br>
|
||||||
|
* 首先尝试使用charset编码读取内容(如果为空默认UTF-8),如果isGetCharsetFromContent为true,则通过正则在正文中获取编码信息,转换为指定编码;
|
||||||
|
*
|
||||||
|
* @param contentBytes 内容byte数组
|
||||||
|
* @param charset 字符集
|
||||||
|
* @param isGetCharsetFromContent 是否从返回内容中获得编码信息
|
||||||
|
* @return 内容
|
||||||
|
*/
|
||||||
|
public static String getString(byte[] contentBytes, Charset charset, boolean isGetCharsetFromContent) {
|
||||||
|
if (null == contentBytes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == charset) {
|
||||||
|
charset = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
}
|
||||||
|
String content = new String(contentBytes, charset);
|
||||||
|
if (isGetCharsetFromContent) {
|
||||||
|
final String charsetInContentStr = ReUtil.get(META_CHARSET_PATTERN, content, 1);
|
||||||
|
if (StrUtil.isNotBlank(charsetInContentStr)) {
|
||||||
|
Charset charsetInContent = null;
|
||||||
|
try {
|
||||||
|
charsetInContent = Charset.forName(charsetInContentStr);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (StrUtil.containsIgnoreCase(charsetInContentStr, "utf-8") || StrUtil.containsIgnoreCase(charsetInContentStr, "utf8")) {
|
||||||
|
charsetInContent = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
} else if (StrUtil.containsIgnoreCase(charsetInContentStr, "gbk")) {
|
||||||
|
charsetInContent = CharsetUtil.CHARSET_GBK;
|
||||||
|
}
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
if (null != charsetInContent && false == charset.equals(charsetInContent)) {
|
||||||
|
content = new String(contentBytes, charsetInContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件扩展名获得MimeType
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径或文件名
|
||||||
|
* @param defaultValue 当获取MimeType为null时的默认值
|
||||||
|
* @return MimeType
|
||||||
|
* @see FileUtil#getMimeType(String)
|
||||||
|
* @since 4.6.5
|
||||||
|
*/
|
||||||
|
public static String getMimeType(String filePath, String defaultValue) {
|
||||||
|
return ObjectUtil.defaultIfNull(getMimeType(filePath), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件扩展名获得MimeType
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径或文件名
|
||||||
|
* @return MimeType
|
||||||
|
* @see FileUtil#getMimeType(String)
|
||||||
|
*/
|
||||||
|
public static String getMimeType(String filePath) {
|
||||||
|
return FileUtil.getMimeType(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求参数的body中判断请求的Content-Type类型,支持的类型有:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. application/json
|
||||||
|
* 1. application/xml
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param body 请求参数体
|
||||||
|
* @return Content-Type类型,如果无法判断返回null
|
||||||
|
* @see aiyh.utils.tool.cn.hutool.http.ContentType#get(String)
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public static String getContentTypeByRequestBody(String body) {
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.ContentType contentType = ContentType.get(body);
|
||||||
|
return (null == contentType) ? null : contentType.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建简易的Http服务器
|
||||||
|
*
|
||||||
|
* @param port 端口
|
||||||
|
* @return {@link SimpleServer}
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public static SimpleServer createServer(int port) {
|
||||||
|
return new SimpleServer(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建简单的账号秘密验证信息,构建后类似于:
|
||||||
|
* <pre>
|
||||||
|
* Basic YWxhZGRpbjpvcGVuc2VzYW1l
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param username 账号
|
||||||
|
* @param password 密码
|
||||||
|
* @param charset 编码(如果账号或密码中有非ASCII字符适用)
|
||||||
|
* @return 密码验证信息
|
||||||
|
* @since 5.4.6
|
||||||
|
*/
|
||||||
|
public static String buildBasicAuth(String username, String password, Charset charset) {
|
||||||
|
final String data = username.concat(":").concat(password);
|
||||||
|
return "Basic " + Base64.encode(data, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭Cookie
|
||||||
|
*
|
||||||
|
* @see GlobalCookieManager#setCookieManager(CookieManager)
|
||||||
|
* @since 5.6.5
|
||||||
|
*/
|
||||||
|
public static void closeCookie() {
|
||||||
|
GlobalCookieManager.setCookieManager(null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http方法枚举
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public enum Method {
|
||||||
|
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT, PATCH
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.convert.Convert;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.resource.MultiResource;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.resource.Resource;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.resource.StringResource;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multipart/form-data输出流封装<br>
|
||||||
|
* 遵循RFC2388规范
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public class MultipartOutputStream extends OutputStream {
|
||||||
|
|
||||||
|
private static final String CONTENT_DISPOSITION_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"\r\n";
|
||||||
|
private static final String CONTENT_DISPOSITION_FILE_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n";
|
||||||
|
|
||||||
|
private static final String CONTENT_TYPE_FILE_TEMPLATE = "Content-Type: {}\r\n";
|
||||||
|
|
||||||
|
private final OutputStream out;
|
||||||
|
private final Charset charset;
|
||||||
|
private final String boundary;
|
||||||
|
|
||||||
|
private boolean isFinish;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,使用全局默认的边界字符串
|
||||||
|
*
|
||||||
|
* @param out HTTP写出流
|
||||||
|
* @param charset 编码
|
||||||
|
*/
|
||||||
|
public MultipartOutputStream(OutputStream out, Charset charset) {
|
||||||
|
this(out, charset, HttpGlobalConfig.getBoundary());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param out HTTP写出流
|
||||||
|
* @param charset 编码
|
||||||
|
* @param boundary 边界符
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public MultipartOutputStream(OutputStream out, Charset charset, String boundary) {
|
||||||
|
this.out = out;
|
||||||
|
this.charset = charset;
|
||||||
|
this.boundary = boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加Multipart表单的数据项<br>
|
||||||
|
* <pre>
|
||||||
|
* --分隔符(boundary)[换行]
|
||||||
|
* Content-Disposition: form-data; name="参数名"[换行]
|
||||||
|
* [换行]
|
||||||
|
* 参数值[换行]
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* 或者:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* --分隔符(boundary)[换行]
|
||||||
|
* Content-Disposition: form-data; name="表单名"; filename="文件名"[换行]
|
||||||
|
* Content-Type: MIME类型[换行]
|
||||||
|
* [换行]
|
||||||
|
* 文件的二进制内容[换行]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param formFieldName 表单名
|
||||||
|
* @param value 值,可以是普通值、资源(如文件等)
|
||||||
|
* @return this
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
public MultipartOutputStream write(String formFieldName, Object value) throws IORuntimeException {
|
||||||
|
// 多资源
|
||||||
|
if (value instanceof MultiResource) {
|
||||||
|
for (Resource subResource : (MultiResource) value) {
|
||||||
|
write(formFieldName, subResource);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --分隔符(boundary)[换行]
|
||||||
|
beginPart();
|
||||||
|
|
||||||
|
if (value instanceof Resource) {
|
||||||
|
appendResource(formFieldName, (Resource) value);
|
||||||
|
} else {
|
||||||
|
appendResource(formFieldName,
|
||||||
|
new StringResource(Convert.toStr(value), null, this.charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
write(StrUtil.CRLF);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
this.out.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传表单结束
|
||||||
|
*
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
public void finish() throws IORuntimeException {
|
||||||
|
if (!isFinish) {
|
||||||
|
write(StrUtil.format("--{}--\r\n", boundary));
|
||||||
|
this.isFinish = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
finish();
|
||||||
|
IoUtil.close(this.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加Multipart表单的Resource数据项,支持包括{@link HttpResource}资源格式
|
||||||
|
*
|
||||||
|
* @param formFieldName 表单名
|
||||||
|
* @param resource 资源
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
private void appendResource(String formFieldName, Resource resource) throws IORuntimeException {
|
||||||
|
final String fileName = resource.getName();
|
||||||
|
|
||||||
|
// Content-Disposition
|
||||||
|
if (null == fileName) {
|
||||||
|
// Content-Disposition: form-data; name="参数名"[换行]
|
||||||
|
write(StrUtil.format(CONTENT_DISPOSITION_TEMPLATE, formFieldName));
|
||||||
|
} else {
|
||||||
|
// Content-Disposition: form-data; name="参数名"; filename="文件名"[换行]
|
||||||
|
write(StrUtil.format(CONTENT_DISPOSITION_FILE_TEMPLATE, formFieldName, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content-Type
|
||||||
|
if (resource instanceof HttpResource) {
|
||||||
|
final String contentType = ((HttpResource) resource).getContentType();
|
||||||
|
if (StrUtil.isNotBlank(contentType)) {
|
||||||
|
// Content-Type: 类型[换行]
|
||||||
|
write(StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE, contentType));
|
||||||
|
}
|
||||||
|
} else if (StrUtil.isNotEmpty(fileName)) {
|
||||||
|
// 根据name的扩展名指定互联网媒体类型,默认二进制流数据
|
||||||
|
write(StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE,
|
||||||
|
HttpUtil.getMimeType(fileName, ContentType.OCTET_STREAM.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
write("\r\n");
|
||||||
|
resource.writeTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* part开始,写出:<br>
|
||||||
|
* <pre>
|
||||||
|
* --分隔符(boundary)[换行]
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
private void beginPart() {
|
||||||
|
// --分隔符(boundary)[换行]
|
||||||
|
write("--", boundary, StrUtil.CRLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出对象
|
||||||
|
*
|
||||||
|
* @param objs 写出的对象(转换为字符串)
|
||||||
|
*/
|
||||||
|
private void write(Object... objs) {
|
||||||
|
IoUtil.write(this, this.charset, false, objs);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回状态码
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
interface Status {
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 200: OK.
|
||||||
|
*/
|
||||||
|
int HTTP_OK = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 201: Created.
|
||||||
|
*/
|
||||||
|
int HTTP_CREATED = 201;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 202: Accepted.
|
||||||
|
*/
|
||||||
|
int HTTP_ACCEPTED = 202;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 203: Non-Authoritative Information.
|
||||||
|
*/
|
||||||
|
int HTTP_NOT_AUTHORITATIVE = 203;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 204: No Content.
|
||||||
|
*/
|
||||||
|
int HTTP_NO_CONTENT = 204;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 205: Reset Content.
|
||||||
|
*/
|
||||||
|
int HTTP_RESET = 205;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 206: Partial Content.
|
||||||
|
*/
|
||||||
|
int HTTP_PARTIAL = 206;
|
||||||
|
|
||||||
|
/* 3XX: relocation/redirect */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 300: Multiple Choices.
|
||||||
|
*/
|
||||||
|
int HTTP_MULT_CHOICE = 300;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 301: Moved Permanently.
|
||||||
|
*/
|
||||||
|
int HTTP_MOVED_PERM = 301;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 302: Temporary Redirect.
|
||||||
|
*/
|
||||||
|
int HTTP_MOVED_TEMP = 302;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 303: See Other.
|
||||||
|
*/
|
||||||
|
int HTTP_SEE_OTHER = 303;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 304: Not Modified.
|
||||||
|
*/
|
||||||
|
int HTTP_NOT_MODIFIED = 304;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 305: Use Proxy.
|
||||||
|
*/
|
||||||
|
int HTTP_USE_PROXY = 305;
|
||||||
|
|
||||||
|
/* 4XX: client error */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 400: Bad Request.
|
||||||
|
*/
|
||||||
|
int HTTP_BAD_REQUEST = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 401: Unauthorized.
|
||||||
|
*/
|
||||||
|
int HTTP_UNAUTHORIZED = 401;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 402: Payment Required.
|
||||||
|
*/
|
||||||
|
int HTTP_PAYMENT_REQUIRED = 402;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 403: Forbidden.
|
||||||
|
*/
|
||||||
|
int HTTP_FORBIDDEN = 403;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 404: Not Found.
|
||||||
|
*/
|
||||||
|
int HTTP_NOT_FOUND = 404;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 405: Method Not Allowed.
|
||||||
|
*/
|
||||||
|
int HTTP_BAD_METHOD = 405;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 406: Not Acceptable.
|
||||||
|
*/
|
||||||
|
int HTTP_NOT_ACCEPTABLE = 406;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 407: Proxy Authentication Required.
|
||||||
|
*/
|
||||||
|
int HTTP_PROXY_AUTH = 407;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 408: Request Time-Out.
|
||||||
|
*/
|
||||||
|
int HTTP_CLIENT_TIMEOUT = 408;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 409: Conflict.
|
||||||
|
*/
|
||||||
|
int HTTP_CONFLICT = 409;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 410: Gone.
|
||||||
|
*/
|
||||||
|
int HTTP_GONE = 410;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 411: Length Required.
|
||||||
|
*/
|
||||||
|
int HTTP_LENGTH_REQUIRED = 411;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 412: Precondition Failed.
|
||||||
|
*/
|
||||||
|
int HTTP_PRECON_FAILED = 412;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 413: Request Entity Too Large.
|
||||||
|
*/
|
||||||
|
int HTTP_ENTITY_TOO_LARGE = 413;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 414: Request-URI Too Large.
|
||||||
|
*/
|
||||||
|
int HTTP_REQ_TOO_LONG = 414;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 415: Unsupported Media Type.
|
||||||
|
*/
|
||||||
|
int HTTP_UNSUPPORTED_TYPE = 415;
|
||||||
|
|
||||||
|
/* 5XX: server error */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 500: Internal Server Error.
|
||||||
|
*/
|
||||||
|
int HTTP_INTERNAL_ERROR = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 501: Not Implemented.
|
||||||
|
*/
|
||||||
|
int HTTP_NOT_IMPLEMENTED = 501;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 502: Bad Gateway.
|
||||||
|
*/
|
||||||
|
int HTTP_BAD_GATEWAY = 502;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 503: Service Unavailable.
|
||||||
|
*/
|
||||||
|
int HTTP_UNAVAILABLE = 503;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 504: Gateway Timeout.
|
||||||
|
*/
|
||||||
|
int HTTP_GATEWAY_TIMEOUT = 504;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP Status-Code 505: HTTP Version Not Supported.
|
||||||
|
*/
|
||||||
|
int HTTP_VERSION = 505;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.body;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bytes类型的Http request body,主要发送编码后的表单数据或rest body(如JSON或XML)
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public class BytesBody implements RequestBody {
|
||||||
|
|
||||||
|
private final byte[] content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Http request body
|
||||||
|
*
|
||||||
|
* @param content body内容,编码后
|
||||||
|
* @return BytesBody
|
||||||
|
*/
|
||||||
|
public static BytesBody create(byte[] content) {
|
||||||
|
return new BytesBody(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param content Body内容,编码后
|
||||||
|
*/
|
||||||
|
public BytesBody(byte[] content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream out) {
|
||||||
|
IoUtil.write(out, false, content);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.body;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.url.UrlQuery;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* application/x-www-form-urlencoded 类型请求body封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
public class FormUrlEncodedBody extends BytesBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 Http request body
|
||||||
|
*
|
||||||
|
* @param form 表单
|
||||||
|
* @param charset 编码
|
||||||
|
* @return FormUrlEncodedBody
|
||||||
|
*/
|
||||||
|
public static FormUrlEncodedBody create(Map<String, Object> form, Charset charset) {
|
||||||
|
return new FormUrlEncodedBody(form, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param form 表单
|
||||||
|
* @param charset 编码
|
||||||
|
*/
|
||||||
|
public FormUrlEncodedBody(Map<String, Object> form, Charset charset) {
|
||||||
|
super(StrUtil.bytes(UrlQuery.of(form, true).build(charset), charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.body;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.ContentType;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpGlobalConfig;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.MultipartOutputStream;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multipart/form-data数据的请求体封装<br>
|
||||||
|
* 遵循RFC2388规范
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.3.5
|
||||||
|
*/
|
||||||
|
public class MultipartBody implements RequestBody {
|
||||||
|
|
||||||
|
private static final String CONTENT_TYPE_MULTIPART_PREFIX = ContentType.MULTIPART.getValue() + "; boundary=";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储表单数据
|
||||||
|
*/
|
||||||
|
private final Map<String, Object> form;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final Charset charset;
|
||||||
|
/**
|
||||||
|
* 边界
|
||||||
|
*/
|
||||||
|
private final String boundary = HttpGlobalConfig.getBoundary();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据已有表单内容,构建MultipartBody
|
||||||
|
*
|
||||||
|
* @param form 表单
|
||||||
|
* @param charset 编码
|
||||||
|
* @return MultipartBody
|
||||||
|
*/
|
||||||
|
public static MultipartBody create(Map<String, Object> form, Charset charset) {
|
||||||
|
return new MultipartBody(form, charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Multipart的Content-Type类型
|
||||||
|
*
|
||||||
|
* @return Multipart的Content-Type类型
|
||||||
|
*/
|
||||||
|
public String getContentType() {
|
||||||
|
return CONTENT_TYPE_MULTIPART_PREFIX + boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param form 表单
|
||||||
|
* @param charset 编码
|
||||||
|
*/
|
||||||
|
public MultipartBody(Map<String, Object> form, Charset charset) {
|
||||||
|
this.form = form;
|
||||||
|
this.charset = charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出Multiparty数据,不关闭流
|
||||||
|
*
|
||||||
|
* @param out out流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream out) {
|
||||||
|
final MultipartOutputStream stream = new MultipartOutputStream(out, this.charset, this.boundary);
|
||||||
|
if (MapUtil.isNotEmpty(this.form)) {
|
||||||
|
this.form.forEach(stream::write);
|
||||||
|
}
|
||||||
|
stream.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
write(out);
|
||||||
|
return IoUtil.toStr(out, this.charset);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.body;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定义请求体接口
|
||||||
|
*/
|
||||||
|
public interface RequestBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据,不关闭流
|
||||||
|
*
|
||||||
|
* @param out out流
|
||||||
|
*/
|
||||||
|
void write(OutputStream out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出并关闭{@link OutputStream}
|
||||||
|
*
|
||||||
|
* @param out {@link OutputStream}
|
||||||
|
* @since 5.7.17
|
||||||
|
*/
|
||||||
|
default void writeClose(OutputStream out) {
|
||||||
|
try {
|
||||||
|
write(out);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* 请求体封装实现
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.body;
|
|
@ -0,0 +1,109 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.cookie;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.URLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpConnection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.CookiePolicy;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局Cookie管理器,只针对Hutool请求有效
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 4.5.15
|
||||||
|
*/
|
||||||
|
public class GlobalCookieManager {
|
||||||
|
|
||||||
|
/** Cookie管理 */
|
||||||
|
private static CookieManager cookieManager;
|
||||||
|
|
||||||
|
static {
|
||||||
|
cookieManager = new CookieManager(new ThreadLocalCookieStore(), CookiePolicy.ACCEPT_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义{@link CookieManager}
|
||||||
|
*
|
||||||
|
* @param customCookieManager 自定义的{@link CookieManager}
|
||||||
|
*/
|
||||||
|
public static void setCookieManager(CookieManager customCookieManager) {
|
||||||
|
cookieManager = customCookieManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局{@link CookieManager}
|
||||||
|
*
|
||||||
|
* @return {@link CookieManager}
|
||||||
|
*/
|
||||||
|
public static CookieManager getCookieManager() {
|
||||||
|
return cookieManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定域名下所有Cookie信息
|
||||||
|
*
|
||||||
|
* @param conn HTTP连接
|
||||||
|
* @return Cookie信息列表
|
||||||
|
* @since 4.6.9
|
||||||
|
*/
|
||||||
|
public static List<HttpCookie> getCookies(aiyh.utils.tool.cn.hutool.http.HttpConnection conn) {
|
||||||
|
return cookieManager.getCookieStore().get(getURI(conn));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将本地存储的Cookie信息附带到Http请求中,不覆盖用户定义好的Cookie
|
||||||
|
*
|
||||||
|
* @param conn {@link aiyh.utils.tool.cn.hutool.http.HttpConnection}
|
||||||
|
*/
|
||||||
|
public static void add(aiyh.utils.tool.cn.hutool.http.HttpConnection conn) {
|
||||||
|
if (null == cookieManager) {
|
||||||
|
// 全局Cookie管理器关闭
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> cookieHeader;
|
||||||
|
try {
|
||||||
|
cookieHeader = cookieManager.get(getURI(conn), new HashMap<>(0));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不覆盖模式回填Cookie头,这样用户定义的Cookie将优先
|
||||||
|
conn.header(cookieHeader, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储响应的Cookie信息到本地
|
||||||
|
*
|
||||||
|
* @param conn {@link aiyh.utils.tool.cn.hutool.http.HttpConnection}
|
||||||
|
*/
|
||||||
|
public static void store(aiyh.utils.tool.cn.hutool.http.HttpConnection conn) {
|
||||||
|
if (null == cookieManager) {
|
||||||
|
// 全局Cookie管理器关闭
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cookieManager.put(getURI(conn), conn.headers());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取连接的URL中URI信息
|
||||||
|
*
|
||||||
|
* @param conn HttpConnection
|
||||||
|
* @return URI
|
||||||
|
*/
|
||||||
|
private static URI getURI(HttpConnection conn) {
|
||||||
|
return URLUtil.toURI(conn.getUrl());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.cookie;
|
||||||
|
|
||||||
|
import java.net.CookieManager;
|
||||||
|
import java.net.CookieStore;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程隔离的Cookie存储。多线程环境下Cookie隔离使用,防止Cookie覆盖<br>
|
||||||
|
* <p>
|
||||||
|
* 见:https://stackoverflow.com/questions/16305486/cookiemanager-for-multiple-threads
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.1.18
|
||||||
|
*/
|
||||||
|
public class ThreadLocalCookieStore implements CookieStore {
|
||||||
|
|
||||||
|
private final static ThreadLocal<CookieStore> STORES = new ThreadLocal<CookieStore>() {
|
||||||
|
@Override
|
||||||
|
protected synchronized CookieStore initialValue() {
|
||||||
|
/* InMemoryCookieStore */
|
||||||
|
return (new CookieManager()).getCookieStore();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本线程下的CookieStore
|
||||||
|
*
|
||||||
|
* @return CookieStore
|
||||||
|
*/
|
||||||
|
public CookieStore getCookieStore() {
|
||||||
|
return STORES.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除当前线程的Cookie
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public ThreadLocalCookieStore removeCurrent() {
|
||||||
|
STORES.remove();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(URI uri, HttpCookie cookie) {
|
||||||
|
getCookieStore().add(uri, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HttpCookie> get(URI uri) {
|
||||||
|
return getCookieStore().get(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HttpCookie> getCookies() {
|
||||||
|
return getCookieStore().getCookies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<URI> getURIs() {
|
||||||
|
return getCookieStore().getURIs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(URI uri, HttpCookie cookie) {
|
||||||
|
return getCookieStore().remove(uri, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll() {
|
||||||
|
return getCookieStore().removeAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* 自定义Cookie
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.cookie;
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单。
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http;
|
|
@ -0,0 +1,57 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharsetUtil;
|
||||||
|
import com.sun.net.httpserver.HttpContext;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HttpServer公用对象,提供HttpExchange包装和公用方法
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public class HttpServerBase implements Closeable {
|
||||||
|
|
||||||
|
final static Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
|
||||||
|
final HttpExchange httpExchange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param httpExchange {@link HttpExchange}
|
||||||
|
*/
|
||||||
|
public HttpServerBase(HttpExchange httpExchange) {
|
||||||
|
this.httpExchange = httpExchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取{@link HttpExchange}对象
|
||||||
|
*
|
||||||
|
* @return {@link HttpExchange}对象
|
||||||
|
*/
|
||||||
|
public HttpExchange getHttpExchange() {
|
||||||
|
return this.httpExchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取{@link HttpContext}
|
||||||
|
*
|
||||||
|
* @return {@link HttpContext}
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public HttpContext getHttpContext() {
|
||||||
|
return getHttpExchange().getHttpContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用{@link HttpExchange#close()},关闭请求流和响应流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this.httpExchange.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,442 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.CaseInsensitiveMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.multi.ListValueMap;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.NetUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.multipart.MultipartFormData;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.multipart.UploadSetting;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharsetUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.Header;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.Method;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.useragent.UserAgent;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.useragent.UserAgentUtil;
|
||||||
|
import com.sun.net.httpserver.Headers;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpCookie;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http请求对象,对{@link HttpExchange}封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public class HttpServerRequest extends HttpServerBase {
|
||||||
|
|
||||||
|
private Map<String, HttpCookie> cookieCache;
|
||||||
|
private ListValueMap<String, String> paramsCache;
|
||||||
|
private MultipartFormData multipartFormDataCache;
|
||||||
|
private Charset charsetCache;
|
||||||
|
private byte[] bodyCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param httpExchange {@link HttpExchange}
|
||||||
|
*/
|
||||||
|
public HttpServerRequest(HttpExchange httpExchange) {
|
||||||
|
super(httpExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Http Method
|
||||||
|
*
|
||||||
|
* @return Http Method
|
||||||
|
*/
|
||||||
|
public String getMethod() {
|
||||||
|
return this.httpExchange.getRequestMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为GET请求
|
||||||
|
*
|
||||||
|
* @return 是否为GET请求
|
||||||
|
*/
|
||||||
|
public boolean isGetMethod() {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.Method.GET.name().equalsIgnoreCase(getMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为POST请求
|
||||||
|
*
|
||||||
|
* @return 是否为POST请求
|
||||||
|
*/
|
||||||
|
public boolean isPostMethod() {
|
||||||
|
return Method.POST.name().equalsIgnoreCase(getMethod());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求URI
|
||||||
|
*
|
||||||
|
* @return 请求URI
|
||||||
|
*/
|
||||||
|
public URI getURI() {
|
||||||
|
return this.httpExchange.getRequestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求路径Path
|
||||||
|
*
|
||||||
|
* @return 请求路径
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return getURI().getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求参数
|
||||||
|
*
|
||||||
|
* @return 参数字符串
|
||||||
|
*/
|
||||||
|
public String getQuery() {
|
||||||
|
return getURI().getQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求header中的信息
|
||||||
|
*
|
||||||
|
* @return header值
|
||||||
|
*/
|
||||||
|
public Headers getHeaders() {
|
||||||
|
return this.httpExchange.getRequestHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求header中的信息
|
||||||
|
*
|
||||||
|
* @param headerKey 头信息的KEY
|
||||||
|
* @return header值
|
||||||
|
*/
|
||||||
|
public String getHeader(aiyh.utils.tool.cn.hutool.http.Header headerKey) {
|
||||||
|
return getHeader(headerKey.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求header中的信息
|
||||||
|
*
|
||||||
|
* @param headerKey 头信息的KEY
|
||||||
|
* @return header值
|
||||||
|
*/
|
||||||
|
public String getHeader(String headerKey) {
|
||||||
|
return getHeaders().getFirst(headerKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得请求header中的信息
|
||||||
|
*
|
||||||
|
* @param headerKey 头信息的KEY
|
||||||
|
* @param charset 字符集
|
||||||
|
* @return header值
|
||||||
|
*/
|
||||||
|
public String getHeader(String headerKey, Charset charset) {
|
||||||
|
final String header = getHeader(headerKey);
|
||||||
|
if (null != header) {
|
||||||
|
return CharsetUtil.convert(header, CharsetUtil.CHARSET_ISO_8859_1, charset);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Content-Type头信息
|
||||||
|
*
|
||||||
|
* @return Content-Type头信息
|
||||||
|
*/
|
||||||
|
public String getContentType() {
|
||||||
|
return getHeader(aiyh.utils.tool.cn.hutool.http.Header.CONTENT_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取编码,获取失败默认使用UTF-8,获取规则如下:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、从Content-Type头中获取编码,类似于:text/html;charset=utf-8
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return 编码,默认UTF-8
|
||||||
|
*/
|
||||||
|
public Charset getCharset() {
|
||||||
|
if (null == this.charsetCache) {
|
||||||
|
final String contentType = getContentType();
|
||||||
|
final String charsetStr = aiyh.utils.tool.cn.hutool.http.HttpUtil.getCharset(contentType);
|
||||||
|
this.charsetCache = CharsetUtil.parse(charsetStr, DEFAULT_CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.charsetCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得User-Agent
|
||||||
|
*
|
||||||
|
* @return User-Agent字符串
|
||||||
|
*/
|
||||||
|
public String getUserAgentStr() {
|
||||||
|
return getHeader(aiyh.utils.tool.cn.hutool.http.Header.USER_AGENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得User-Agent,未识别返回null
|
||||||
|
*
|
||||||
|
* @return User-Agent字符串,未识别返回null
|
||||||
|
*/
|
||||||
|
public UserAgent getUserAgent() {
|
||||||
|
return UserAgentUtil.parse(getUserAgentStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Cookie信息字符串
|
||||||
|
*
|
||||||
|
* @return cookie字符串
|
||||||
|
*/
|
||||||
|
public String getCookiesStr() {
|
||||||
|
return getHeader(Header.COOKIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Cookie信息列表
|
||||||
|
*
|
||||||
|
* @return Cookie信息列表
|
||||||
|
*/
|
||||||
|
public Collection<HttpCookie> getCookies() {
|
||||||
|
return getCookieMap().values();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得Cookie信息Map,键为Cookie名,值为HttpCookie对象
|
||||||
|
*
|
||||||
|
* @return Cookie信息Map
|
||||||
|
*/
|
||||||
|
public Map<String, HttpCookie> getCookieMap() {
|
||||||
|
if (null == this.cookieCache) {
|
||||||
|
cookieCache = Collections.unmodifiableMap(CollUtil.toMap(
|
||||||
|
NetUtil.parseCookies(getCookiesStr()),
|
||||||
|
new CaseInsensitiveMap<>(),
|
||||||
|
HttpCookie::getName));
|
||||||
|
}
|
||||||
|
return cookieCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定Cookie名对应的HttpCookie对象
|
||||||
|
*
|
||||||
|
* @param cookieName Cookie名
|
||||||
|
* @return HttpCookie对象
|
||||||
|
*/
|
||||||
|
public HttpCookie getCookie(String cookieName) {
|
||||||
|
return getCookieMap().get(cookieName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Multipart类型表单,此类型表单用于文件上传
|
||||||
|
*
|
||||||
|
* @return 是否为Multipart类型表单,此类型表单用于文件上传
|
||||||
|
*/
|
||||||
|
public boolean isMultipart() {
|
||||||
|
if (!isPostMethod()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String contentType = getContentType();
|
||||||
|
if (StrUtil.isBlank(contentType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return contentType.toLowerCase().startsWith("multipart/");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求体文本,可以是form表单、json、xml等任意内容<br>
|
||||||
|
* 使用{@link #getCharset()}判断编码,判断失败使用UTF-8编码
|
||||||
|
*
|
||||||
|
* @return 请求
|
||||||
|
*/
|
||||||
|
public String getBody() {
|
||||||
|
return getBody(getCharset());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求体文本,可以是form表单、json、xml等任意内容
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 请求
|
||||||
|
*/
|
||||||
|
public String getBody(Charset charset) {
|
||||||
|
return StrUtil.str(getBodyBytes(), charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取body的bytes数组
|
||||||
|
*
|
||||||
|
* @return body的bytes数组
|
||||||
|
*/
|
||||||
|
public byte[] getBodyBytes() {
|
||||||
|
if (null == this.bodyCache) {
|
||||||
|
this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
|
||||||
|
}
|
||||||
|
return this.bodyCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
|
||||||
|
*
|
||||||
|
* @return 流
|
||||||
|
*/
|
||||||
|
public InputStream getBodyStream() {
|
||||||
|
return this.httpExchange.getRequestBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定名称的参数值,取第一个值
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @return 参数值
|
||||||
|
* @since 5.5.8
|
||||||
|
*/
|
||||||
|
public String getParam(String name) {
|
||||||
|
return getParams().get(name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定名称的参数值
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @return 参数值
|
||||||
|
* @since 5.5.8
|
||||||
|
*/
|
||||||
|
public List<String> getParams(String name) {
|
||||||
|
return getParams().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数Map
|
||||||
|
*
|
||||||
|
* @return 参数map
|
||||||
|
*/
|
||||||
|
public ListValueMap<String, String> getParams() {
|
||||||
|
if (null == this.paramsCache) {
|
||||||
|
this.paramsCache = new ListValueMap<>();
|
||||||
|
final Charset charset = getCharset();
|
||||||
|
|
||||||
|
// 解析URL中的参数
|
||||||
|
final String query = getQuery();
|
||||||
|
if (StrUtil.isNotBlank(query)) {
|
||||||
|
this.paramsCache.putAll(aiyh.utils.tool.cn.hutool.http.HttpUtil.decodeParams(query, charset, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析multipart中的参数
|
||||||
|
if (isMultipart()) {
|
||||||
|
this.paramsCache.putAll(getMultipart().getParamListMap());
|
||||||
|
} else {
|
||||||
|
// 解析body中的参数
|
||||||
|
final String body = getBody();
|
||||||
|
if (StrUtil.isNotBlank(body)) {
|
||||||
|
this.paramsCache.putAll(HttpUtil.decodeParams(body, charset, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.paramsCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端IP
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 默认检测的Header:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1、X-Forwarded-For
|
||||||
|
* 2、X-Real-IP
|
||||||
|
* 3、Proxy-Client-IP
|
||||||
|
* 4、WL-Proxy-Client-IP
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* otherHeaderNames参数用于自定义检测的Header<br>
|
||||||
|
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param otherHeaderNames 其他自定义头文件,通常在Http服务器(例如Nginx)中配置
|
||||||
|
* @return IP地址
|
||||||
|
*/
|
||||||
|
public String getClientIP(String... otherHeaderNames) {
|
||||||
|
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
|
||||||
|
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
|
||||||
|
headers = ArrayUtil.addAll(headers, otherHeaderNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getClientIPByHeader(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端IP
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* headerNames参数用于自定义检测的Header<br>
|
||||||
|
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param headerNames 自定义头,通常在Http服务器(例如Nginx)中配置
|
||||||
|
* @return IP地址
|
||||||
|
* @since 4.4.1
|
||||||
|
*/
|
||||||
|
public String getClientIPByHeader(String... headerNames) {
|
||||||
|
String ip;
|
||||||
|
for (String header : headerNames) {
|
||||||
|
ip = getHeader(header);
|
||||||
|
if (!NetUtil.isUnknown(ip)) {
|
||||||
|
return NetUtil.getMultistageReverseProxyIp(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = this.httpExchange.getRemoteAddress().getHostName();
|
||||||
|
return NetUtil.getMultistageReverseProxyIp(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得MultiPart表单内容,多用于获得上传的文件
|
||||||
|
*
|
||||||
|
* @return MultipartFormData
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
* @since 5.3.0
|
||||||
|
*/
|
||||||
|
public MultipartFormData getMultipart() throws IORuntimeException {
|
||||||
|
if (null == this.multipartFormDataCache) {
|
||||||
|
this.multipartFormDataCache = parseMultipart(new UploadSetting());
|
||||||
|
}
|
||||||
|
return this.multipartFormDataCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得multipart/form-data 表单内容<br>
|
||||||
|
* 包括文件和普通表单数据<br>
|
||||||
|
* 在同一次请求中,此方法只能被执行一次!
|
||||||
|
*
|
||||||
|
* @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
|
||||||
|
* @return MultiPart表单
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
* @since 5.3.0
|
||||||
|
*/
|
||||||
|
public MultipartFormData parseMultipart(UploadSetting uploadSetting) throws IORuntimeException {
|
||||||
|
final MultipartFormData formData = new MultipartFormData(uploadSetting);
|
||||||
|
try {
|
||||||
|
formData.parseRequestStream(getBodyStream(), getCharset());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,429 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FileUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.URLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.ContentType;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.Header;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpStatus;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpUtil;
|
||||||
|
import com.sun.net.httpserver.Headers;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Http响应对象,用于写出数据到客户端
|
||||||
|
*/
|
||||||
|
public class HttpServerResponse extends HttpServerBase {
|
||||||
|
|
||||||
|
private Charset charset;
|
||||||
|
/**
|
||||||
|
* 是否已经发送了Http状态码,如果没有,提前写出状态码
|
||||||
|
*/
|
||||||
|
private boolean isSendCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param httpExchange {@link HttpExchange}
|
||||||
|
*/
|
||||||
|
public HttpServerResponse(HttpExchange httpExchange) {
|
||||||
|
super(httpExchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送HTTP状态码,Content-Length为0不定长度,会输出Transfer-encoding: chunked
|
||||||
|
*
|
||||||
|
* @param httpStatusCode HTTP状态码,见HttpStatus
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse send(int httpStatusCode) {
|
||||||
|
return send(httpStatusCode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送成功状态码
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse sendOk() {
|
||||||
|
return send(aiyh.utils.tool.cn.hutool.http.HttpStatus.HTTP_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送成功状态码
|
||||||
|
*
|
||||||
|
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||||
|
* @return this
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public HttpServerResponse sendOk(int bodyLength) {
|
||||||
|
return send(aiyh.utils.tool.cn.hutool.http.HttpStatus.HTTP_OK, bodyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送404错误页
|
||||||
|
*
|
||||||
|
* @param content 错误页页面内容,默认text/html类型
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse send404(String content) {
|
||||||
|
return sendError(HttpStatus.HTTP_NOT_FOUND, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送错误页
|
||||||
|
*
|
||||||
|
* @param errorCode HTTP错误状态码,见HttpStatus
|
||||||
|
* @param content 错误页页面内容,默认text/html类型
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse sendError(int errorCode, String content) {
|
||||||
|
send(errorCode);
|
||||||
|
setContentType(aiyh.utils.tool.cn.hutool.http.ContentType.TEXT_HTML.toString());
|
||||||
|
return write(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送HTTP状态码
|
||||||
|
*
|
||||||
|
* @param httpStatusCode HTTP状态码,见HttpStatus
|
||||||
|
* @param bodyLength 响应体长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse send(int httpStatusCode, long bodyLength) {
|
||||||
|
if (this.isSendCode) {
|
||||||
|
throw new IORuntimeException("Http status code has been send!");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.httpExchange.sendResponseHeaders(httpStatusCode, bodyLength);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSendCode = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得所有响应头,获取后可以添加新的响应头
|
||||||
|
*
|
||||||
|
* @return 响应头
|
||||||
|
*/
|
||||||
|
public Headers getHeaders() {
|
||||||
|
return this.httpExchange.getResponseHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加响应头,如果已经存在,则追加
|
||||||
|
*
|
||||||
|
* @param header 头key
|
||||||
|
* @param value 值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse addHeader(String header, String value) {
|
||||||
|
getHeaders().add(header, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置响应头,如果已经存在,则覆盖
|
||||||
|
*
|
||||||
|
* @param header 头key
|
||||||
|
* @param value 值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setHeader(aiyh.utils.tool.cn.hutool.http.Header header, String value) {
|
||||||
|
return setHeader(header.getValue(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置响应头,如果已经存在,则覆盖
|
||||||
|
*
|
||||||
|
* @param header 头key
|
||||||
|
* @param value 值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setHeader(String header, String value) {
|
||||||
|
getHeaders().set(header, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置响应头,如果已经存在,则覆盖
|
||||||
|
*
|
||||||
|
* @param header 头key
|
||||||
|
* @param value 值列表
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setHeader(String header, List<String> value) {
|
||||||
|
getHeaders().put(header, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置所有响应头,如果已经存在,则覆盖
|
||||||
|
*
|
||||||
|
* @param headers 响应头map
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setHeaders(Map<String, List<String>> headers) {
|
||||||
|
getHeaders().putAll(headers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Content-Type头,类似于:text/html;charset=utf-8<br>
|
||||||
|
* 如果用户传入的信息无charset信息,自动根据charset补充,charset设置见{@link #setCharset(Charset)}
|
||||||
|
*
|
||||||
|
* @param contentType Content-Type头内容
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setContentType(String contentType) {
|
||||||
|
if (null != contentType && null != this.charset) {
|
||||||
|
if (!contentType.contains(";charset=")) {
|
||||||
|
contentType = ContentType.build(contentType, this.charset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setHeader(aiyh.utils.tool.cn.hutool.http.Header.CONTENT_TYPE, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Content-Length头
|
||||||
|
*
|
||||||
|
* @param contentLength Content-Length头内容
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setContentLength(long contentLength) {
|
||||||
|
return setHeader(aiyh.utils.tool.cn.hutool.http.Header.CONTENT_LENGTH, String.valueOf(contentLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置响应的编码
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setCharset(Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性
|
||||||
|
*
|
||||||
|
* @param name 属性名
|
||||||
|
* @param value 属性值
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse setAttr(String name, Object value) {
|
||||||
|
this.httpExchange.setAttribute(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应数据流
|
||||||
|
*
|
||||||
|
* @return 响应数据流
|
||||||
|
*/
|
||||||
|
public OutputStream getOut() {
|
||||||
|
if (!this.isSendCode) {
|
||||||
|
sendOk();
|
||||||
|
}
|
||||||
|
return this.httpExchange.getResponseBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应数据流
|
||||||
|
*
|
||||||
|
* @return 响应数据流
|
||||||
|
*/
|
||||||
|
public PrintWriter getWriter() {
|
||||||
|
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||||
|
return new PrintWriter(new OutputStreamWriter(getOut(), charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param contentType Content-Type类型
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(String data, String contentType) {
|
||||||
|
setContentType(contentType);
|
||||||
|
return write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(String data) {
|
||||||
|
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||||
|
return write(StrUtil.bytes(data, charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param contentType 返回的类型
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(byte[] data, String contentType) {
|
||||||
|
setContentType(contentType);
|
||||||
|
return write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(byte[] data) {
|
||||||
|
final ByteArrayInputStream in = new ByteArrayInputStream(data);
|
||||||
|
return write(in, in.available());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回数据给客户端
|
||||||
|
*
|
||||||
|
* @param in 需要返回客户端的内容
|
||||||
|
* @param contentType 返回的类型
|
||||||
|
* @return this
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(InputStream in, String contentType) {
|
||||||
|
return write(in, 0, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回数据给客户端
|
||||||
|
*
|
||||||
|
* @param in 需要返回客户端的内容
|
||||||
|
* @param length 内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||||
|
* @param contentType 返回的类型
|
||||||
|
* @return this
|
||||||
|
* @since 5.2.7
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(InputStream in, int length, String contentType) {
|
||||||
|
setContentType(contentType);
|
||||||
|
return write(in, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param in 数据流
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(InputStream in) {
|
||||||
|
return write(in, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写出数据到客户端
|
||||||
|
*
|
||||||
|
* @param in 数据流
|
||||||
|
* @param length 指定响应内容长度,默认0表示不定长度,会输出Transfer-encoding: chunked
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(InputStream in, int length) {
|
||||||
|
if (!isSendCode) {
|
||||||
|
sendOk(Math.max(0, length));
|
||||||
|
}
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = this.httpExchange.getResponseBody();
|
||||||
|
IoUtil.copy(in, out);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(out);
|
||||||
|
IoUtil.close(in);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回文件给客户端(文件下载)
|
||||||
|
*
|
||||||
|
* @param file 写出的文件对象
|
||||||
|
* @return this
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(File file) {
|
||||||
|
return write(file, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回文件给客户端(文件下载)
|
||||||
|
*
|
||||||
|
* @param file 写出的文件对象
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @return this
|
||||||
|
* @since 5.5.8
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(File file, String fileName) {
|
||||||
|
final long fileSize = file.length();
|
||||||
|
if (fileSize > Integer.MAX_VALUE) {
|
||||||
|
throw new IllegalArgumentException("File size is too bigger than " + Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(fileName)) {
|
||||||
|
fileName = file.getName();
|
||||||
|
}
|
||||||
|
final String contentType = ObjectUtil.defaultIfNull(HttpUtil.getMimeType(fileName), "application/octet-stream");
|
||||||
|
BufferedInputStream in = null;
|
||||||
|
try {
|
||||||
|
in = FileUtil.getInputStream(file);
|
||||||
|
write(in, (int) fileSize, contentType, fileName);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(in);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回文件数据给客户端(文件下载)
|
||||||
|
*
|
||||||
|
* @param in 需要返回客户端的内容
|
||||||
|
* @param contentType 返回的类型
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public void write(InputStream in, String contentType, String fileName) {
|
||||||
|
write(in, 0, contentType, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回文件数据给客户端(文件下载)
|
||||||
|
*
|
||||||
|
* @param in 需要返回客户端的内容
|
||||||
|
* @param length 长度
|
||||||
|
* @param contentType 返回的类型
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @return this
|
||||||
|
* @since 5.2.7
|
||||||
|
*/
|
||||||
|
public HttpServerResponse write(InputStream in, int length, String contentType, String fileName) {
|
||||||
|
final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
|
||||||
|
|
||||||
|
if (!contentType.startsWith("text/")) {
|
||||||
|
// 非文本类型数据直接走下载
|
||||||
|
setHeader(Header.CONTENT_DISPOSITION, StrUtil.format("attachment;filename={}", URLUtil.encode(fileName, charset)));
|
||||||
|
}
|
||||||
|
return write(in, length, contentType);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.lang.Console;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.thread.GlobalThreadPool;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.action.Action;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.action.RootAction;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.filter.HttpFilter;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.filter.SimpleFilter;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.handler.ActionHandler;
|
||||||
|
import com.sun.net.httpserver.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简易Http服务器,基于{@link HttpServer}
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.5
|
||||||
|
*/
|
||||||
|
public class SimpleServer {
|
||||||
|
|
||||||
|
private final HttpServer server;
|
||||||
|
private final List<Filter> filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param port 监听端口
|
||||||
|
*/
|
||||||
|
public SimpleServer(int port) {
|
||||||
|
this(new InetSocketAddress(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param hostname 监听地址
|
||||||
|
* @param port 监听端口
|
||||||
|
*/
|
||||||
|
public SimpleServer(String hostname, int port) {
|
||||||
|
this(new InetSocketAddress(hostname, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param address 监听地址
|
||||||
|
*/
|
||||||
|
public SimpleServer(InetSocketAddress address) {
|
||||||
|
this(address, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param address 监听地址
|
||||||
|
* @param configurator https配置信息,用于使用自定义SSL(TLS)证书等
|
||||||
|
*/
|
||||||
|
public SimpleServer(InetSocketAddress address, HttpsConfigurator configurator) {
|
||||||
|
try {
|
||||||
|
if (null != configurator) {
|
||||||
|
final HttpsServer server = HttpsServer.create(address, 0);
|
||||||
|
server.setHttpsConfigurator(configurator);
|
||||||
|
this.server = server;
|
||||||
|
} else {
|
||||||
|
this.server = HttpServer.create(address, 0);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IORuntimeException(e);
|
||||||
|
}
|
||||||
|
setExecutor(GlobalThreadPool.getExecutor());
|
||||||
|
filters = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加请求过滤器,此过滤器对所有请求有效<br>
|
||||||
|
* 此方法需在以下方法前之前调用:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #setRoot(File)} </li>
|
||||||
|
* <li>{@link #setRoot(String)} </li>
|
||||||
|
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||||
|
* <li>{@link #addHandler(String, HttpHandler)}</li>
|
||||||
|
* <li>{@link #addAction(String, Action)} </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param filter {@link Filter} 请求过滤器
|
||||||
|
* @return this
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public SimpleServer addFilter(Filter filter) {
|
||||||
|
this.filters.add(filter);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加请求过滤器,此过滤器对所有请求有效<br>
|
||||||
|
* 此方法需在以下方法前之前调用:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #setRoot(File)} </li>
|
||||||
|
* <li>{@link #setRoot(String)} </li>
|
||||||
|
* <li>{@link #createContext(String, HttpHandler)} </li>
|
||||||
|
* <li>{@link #addHandler(String, HttpHandler)}</li>
|
||||||
|
* <li>{@link #addAction(String, Action)} </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param filter {@link Filter} 请求过滤器
|
||||||
|
* @return this
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public SimpleServer addFilter(HttpFilter filter) {
|
||||||
|
return addFilter(new SimpleFilter() {
|
||||||
|
@Override
|
||||||
|
public void doFilter(HttpExchange httpExchange, Chain chain) throws IOException {
|
||||||
|
filter.doFilter(new HttpServerRequest(httpExchange), new HttpServerResponse(httpExchange), chain);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加请求处理规则
|
||||||
|
*
|
||||||
|
* @param path 路径,例如:/a/b 或者 a/b
|
||||||
|
* @param handler 处理器,包括请求和响应处理
|
||||||
|
* @return this
|
||||||
|
* @see #createContext(String, HttpHandler)
|
||||||
|
*/
|
||||||
|
public SimpleServer addHandler(String path, HttpHandler handler) {
|
||||||
|
createContext(path, handler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建请求映射上下文,创建后,用户访问指定路径可使用{@link HttpHandler} 中的规则进行处理
|
||||||
|
*
|
||||||
|
* @param path 路径,例如:/a/b 或者 a/b
|
||||||
|
* @param handler 处理器,包括请求和响应处理
|
||||||
|
* @return {@link HttpContext}
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public HttpContext createContext(String path, HttpHandler handler) {
|
||||||
|
// 非/开头的路径会报错
|
||||||
|
path = StrUtil.addPrefixIfNot(path, StrUtil.SLASH);
|
||||||
|
final HttpContext context = this.server.createContext(path, handler);
|
||||||
|
// 增加整体过滤器
|
||||||
|
context.getFilters().addAll(this.filters);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置根目录,默认的页面从root目录中读取解析返回
|
||||||
|
*
|
||||||
|
* @param root 路径
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SimpleServer setRoot(String root) {
|
||||||
|
return setRoot(new File(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置根目录,默认的页面从root目录中读取解析返回
|
||||||
|
*
|
||||||
|
* @param root 路径
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SimpleServer setRoot(File root) {
|
||||||
|
return addAction("/", new RootAction(root));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加请求处理规则
|
||||||
|
*
|
||||||
|
* @param path 路径
|
||||||
|
* @param action 处理器
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SimpleServer addAction(String path, Action action) {
|
||||||
|
return addHandler(path, new ActionHandler(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置自定义线程池
|
||||||
|
*
|
||||||
|
* @param executor {@link Executor}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SimpleServer setExecutor(Executor executor) {
|
||||||
|
this.server.setExecutor(executor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得原始HttpServer对象
|
||||||
|
*
|
||||||
|
* @return {@link HttpServer}
|
||||||
|
*/
|
||||||
|
public HttpServer getRawServer() {
|
||||||
|
return this.server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器地址信息
|
||||||
|
*
|
||||||
|
* @return {@link InetSocketAddress}
|
||||||
|
*/
|
||||||
|
public InetSocketAddress getAddress() {
|
||||||
|
return this.server.getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动Http服务器,启动后会阻塞当前线程
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
final InetSocketAddress address = getAddress();
|
||||||
|
Console.log("Hutool Simple Http Server listen on 【{}:{}】", address.getHostName(), address.getPort());
|
||||||
|
this.server.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.action;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerRequest;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerResponse;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求处理接口<br>
|
||||||
|
* 当用户请求某个Path,则调用相应Action的doAction方法
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Action {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求
|
||||||
|
*
|
||||||
|
* @param request 请求对象
|
||||||
|
* @param response 响应对象
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.action;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.FileUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerRequest;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerResponse;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的处理器,通过解析用户传入的path,找到网页根目录下对应文件后返回
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public class RootAction implements Action {
|
||||||
|
|
||||||
|
public static final String DEFAULT_INDEX_FILE_NAME = "index.html";
|
||||||
|
|
||||||
|
private final File rootDir;
|
||||||
|
private final List<String> indexFileNames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param rootDir 网页根目录
|
||||||
|
*/
|
||||||
|
public RootAction(String rootDir) {
|
||||||
|
this(new File(rootDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param rootDir 网页根目录
|
||||||
|
*/
|
||||||
|
public RootAction(File rootDir) {
|
||||||
|
this(rootDir, DEFAULT_INDEX_FILE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param rootDir 网页根目录
|
||||||
|
* @param indexFileNames 主页文件名列表
|
||||||
|
*/
|
||||||
|
public RootAction(String rootDir, String... indexFileNames) {
|
||||||
|
this(new File(rootDir), indexFileNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param rootDir 网页根目录
|
||||||
|
* @param indexFileNames 主页文件名列表
|
||||||
|
* @since 5.4.0
|
||||||
|
*/
|
||||||
|
public RootAction(File rootDir, String... indexFileNames) {
|
||||||
|
this.rootDir = rootDir;
|
||||||
|
this.indexFileNames = CollUtil.toList(indexFileNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(HttpServerRequest request, HttpServerResponse response) {
|
||||||
|
final String path = request.getPath();
|
||||||
|
|
||||||
|
File file = FileUtil.file(rootDir, path);
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
for (String indexFileName : indexFileNames) {
|
||||||
|
// 默认读取主页
|
||||||
|
file = FileUtil.file(file, indexFileName);
|
||||||
|
if (file.exists() && file.isFile()) {
|
||||||
|
response.write(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final String name = request.getParam("name");
|
||||||
|
response.write(file, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response.send404("404 Not Found !");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* {@link com.sun.net.httpserver.HttpServer} 封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.action;
|
|
@ -0,0 +1,27 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.filter;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerRequest;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerResponse;
|
||||||
|
import com.sun.net.httpserver.Filter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤器接口,用于简化{@link Filter} 使用
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface HttpFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行过滤
|
||||||
|
*
|
||||||
|
* @param req {@link aiyh.utils.tool.cn.hutool.http.server.HttpServerRequest} 请求对象,用于获取请求内容
|
||||||
|
* @param res {@link aiyh.utils.tool.cn.hutool.http.server.HttpServerResponse} 响应对象,用于写出内容
|
||||||
|
* @param chain {@link Filter.Chain}
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
void doFilter(HttpServerRequest req, HttpServerResponse res, Filter.Chain chain) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.filter;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.Filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匿名简单过滤器,跳过了描述
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.5.7
|
||||||
|
*/
|
||||||
|
public abstract class SimpleFilter extends Filter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String description() {
|
||||||
|
return "Anonymous Filter";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* {@link com.sun.net.httpserver.Filter} 实现包装
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.filter;
|
|
@ -0,0 +1,38 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.handler;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerRequest;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.HttpServerResponse;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.server.action.Action;
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action处理器,用于将HttpHandler转换为Action形式
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.2.6
|
||||||
|
*/
|
||||||
|
public class ActionHandler implements HttpHandler {
|
||||||
|
|
||||||
|
private final Action action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param action Action
|
||||||
|
*/
|
||||||
|
public ActionHandler(Action action) {
|
||||||
|
this.action = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(HttpExchange httpExchange) throws IOException {
|
||||||
|
action.doAction(
|
||||||
|
new HttpServerRequest(httpExchange),
|
||||||
|
new HttpServerResponse(httpExchange)
|
||||||
|
);
|
||||||
|
httpExchange.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* {@link com.sun.net.httpserver.HttpHandler} 实现包装
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server.handler;
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* Http服务器封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.server;
|
|
@ -0,0 +1,25 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.SSLProtocols;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容android低版本SSL连接<br>
|
||||||
|
* 在测试HttpUrlConnection的时候,发现一部分手机无法连接[GithubPage]
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 最后发现原来是某些SSL协议没有开启
|
||||||
|
*
|
||||||
|
* @author MikaGuraNTK
|
||||||
|
*/
|
||||||
|
public class AndroidSupportSSLFactory extends CustomProtocolsSSLFactory {
|
||||||
|
|
||||||
|
// Android低版本不重置的话某些SSL访问就会失败
|
||||||
|
private static final String[] protocols = {
|
||||||
|
SSLProtocols.SSLv3, SSLProtocols.TLSv1, SSLProtocols.TLSv11, SSLProtocols.TLSv12};
|
||||||
|
|
||||||
|
public AndroidSupportSSLFactory() throws IORuntimeException {
|
||||||
|
super(protocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IORuntimeException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.SSLUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义支持协议类型的SSLSocketFactory
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class CustomProtocolsSSLFactory extends SSLSocketFactory {
|
||||||
|
|
||||||
|
private final String[] protocols;
|
||||||
|
private final SSLSocketFactory base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param protocols 支持协议列表
|
||||||
|
* @throws IORuntimeException IO异常
|
||||||
|
*/
|
||||||
|
public CustomProtocolsSSLFactory(String... protocols) throws IORuntimeException {
|
||||||
|
this.protocols = protocols;
|
||||||
|
this.base = SSLUtil.createSSLContext(null).getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDefaultCipherSuites() {
|
||||||
|
return base.getDefaultCipherSuites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getSupportedCipherSuites() {
|
||||||
|
return base.getSupportedCipherSuites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket() throws IOException {
|
||||||
|
final SSLSocket sslSocket = (SSLSocket) base.createSocket();
|
||||||
|
resetProtocols(sslSocket);
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||||
|
final SSLSocket socket = (SSLSocket) base.createSocket(s, host, port, autoClose);
|
||||||
|
resetProtocols(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port) throws IOException {
|
||||||
|
final SSLSocket socket = (SSLSocket) base.createSocket(host, port);
|
||||||
|
resetProtocols(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
|
||||||
|
final SSLSocket socket = (SSLSocket) base.createSocket(host, port, localHost, localPort);
|
||||||
|
resetProtocols(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||||
|
final SSLSocket socket = (SSLSocket) base.createSocket(host, port);
|
||||||
|
resetProtocols(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||||
|
final SSLSocket socket = (SSLSocket) base.createSocket(address, port, localAddress, localPort);
|
||||||
|
resetProtocols(socket);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置可用策略
|
||||||
|
*
|
||||||
|
* @param socket SSLSocket
|
||||||
|
*/
|
||||||
|
private void resetProtocols(SSLSocket socket) {
|
||||||
|
if (ArrayUtil.isNotEmpty(this.protocols)) {
|
||||||
|
socket.setEnabledProtocols(this.protocols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的SSLSocketFactory
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @since 5.1.2
|
||||||
|
*/
|
||||||
|
public class DefaultSSLFactory extends CustomProtocolsSSLFactory {
|
||||||
|
|
||||||
|
public DefaultSSLFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的全局SSL配置,当用户未设置相关信息时,使用默认设置,默认设置为单例模式。
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.1.2
|
||||||
|
*/
|
||||||
|
public class DefaultSSLInfo {
|
||||||
|
/**
|
||||||
|
* 默认信任全部的域名校验器
|
||||||
|
*/
|
||||||
|
public static final aiyh.utils.tool.cn.hutool.http.ssl.TrustAnyHostnameVerifier TRUST_ANY_HOSTNAME_VERIFIER;
|
||||||
|
/**
|
||||||
|
* 默认的SSLSocketFactory,区分安卓
|
||||||
|
*/
|
||||||
|
public static final SSLSocketFactory DEFAULT_SSF;
|
||||||
|
|
||||||
|
static {
|
||||||
|
TRUST_ANY_HOSTNAME_VERIFIER = new TrustAnyHostnameVerifier();
|
||||||
|
if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) {
|
||||||
|
// 兼容android低版本SSL连接
|
||||||
|
DEFAULT_SSF = new AndroidSupportSSLFactory();
|
||||||
|
} else {
|
||||||
|
DEFAULT_SSF = new DefaultSSLFactory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.SSLContextBuilder;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.net.SSLProtocols;
|
||||||
|
|
||||||
|
import javax.net.ssl.KeyManager;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSLSocketFactory构建器
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
* @see SSLContextBuilder
|
||||||
|
* @deprecated 请使用 {@link SSLContextBuilder}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class SSLSocketFactoryBuilder implements SSLProtocols {
|
||||||
|
|
||||||
|
SSLContextBuilder sslContextBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public SSLSocketFactoryBuilder() {
|
||||||
|
this.sslContextBuilder = SSLContextBuilder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建 SSLSocketFactoryBuilder
|
||||||
|
*
|
||||||
|
* @return SSLSocketFactoryBuilder
|
||||||
|
*/
|
||||||
|
public static SSLSocketFactoryBuilder create() {
|
||||||
|
return new SSLSocketFactoryBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置协议
|
||||||
|
*
|
||||||
|
* @param protocol 协议
|
||||||
|
* @return 自身
|
||||||
|
*/
|
||||||
|
public SSLSocketFactoryBuilder setProtocol(String protocol) {
|
||||||
|
this.sslContextBuilder.setProtocol(protocol);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置信任信息
|
||||||
|
*
|
||||||
|
* @param trustManagers TrustManager列表
|
||||||
|
* @return 自身
|
||||||
|
*/
|
||||||
|
public SSLSocketFactoryBuilder setTrustManagers(TrustManager... trustManagers) {
|
||||||
|
this.sslContextBuilder.setTrustManagers(trustManagers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 JSSE key managers
|
||||||
|
*
|
||||||
|
* @param keyManagers JSSE key managers
|
||||||
|
* @return 自身
|
||||||
|
*/
|
||||||
|
public SSLSocketFactoryBuilder setKeyManagers(KeyManager... keyManagers) {
|
||||||
|
this.sslContextBuilder.setKeyManagers(keyManagers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 SecureRandom
|
||||||
|
*
|
||||||
|
* @param secureRandom SecureRandom
|
||||||
|
* @return 自己
|
||||||
|
*/
|
||||||
|
public SSLSocketFactoryBuilder setSecureRandom(SecureRandom secureRandom) {
|
||||||
|
this.sslContextBuilder.setSecureRandom(secureRandom);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建SSLSocketFactory
|
||||||
|
*
|
||||||
|
* @return SSLSocketFactory
|
||||||
|
* @throws NoSuchAlgorithmException 无此算法
|
||||||
|
* @throws KeyManagementException Key管理异常
|
||||||
|
*/
|
||||||
|
public SSLSocketFactory build() throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
return this.sslContextBuilder.buildChecked().getSocketFactory();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
||||||
|
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https 域名校验
|
||||||
|
*
|
||||||
|
* @author Looly
|
||||||
|
*/
|
||||||
|
public class TrustAnyHostnameVerifier implements HostnameVerifier {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
|
return true;// 直接返回true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* SSL封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.ssl;
|
|
@ -0,0 +1,140 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浏览器对象
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class Browser extends UserAgentInfo {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未知
|
||||||
|
*/
|
||||||
|
public static final Browser Unknown = new Browser(NameUnknown, null, null);
|
||||||
|
/**
|
||||||
|
* 其它版本
|
||||||
|
*/
|
||||||
|
public static final String Other_Version = "[\\/ ]([\\d\\w\\.\\-]+)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的浏览器类型
|
||||||
|
*/
|
||||||
|
public static final List<Browser> browers = CollUtil.newArrayList(
|
||||||
|
// 部分特殊浏览器是基于安卓、Iphone等的,需要优先判断
|
||||||
|
// 企业微信 企业微信使用微信浏览器内核,会包含 MicroMessenger 所以要放在前面
|
||||||
|
new Browser("wxwork", "wxwork", "wxwork\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
// 微信
|
||||||
|
new Browser("MicroMessenger", "MicroMessenger", Other_Version),
|
||||||
|
// 微信小程序
|
||||||
|
new Browser("miniProgram", "miniProgram", Other_Version),
|
||||||
|
// QQ浏览器
|
||||||
|
new Browser("QQBrowser", "MQQBrowser", "MQQBrowser\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
// 钉钉PC端浏览器
|
||||||
|
new Browser("DingTalk-win", "dingtalk-win", "DingTalk\\(([\\d\\w\\.\\-]+)\\)"),
|
||||||
|
// 钉钉内置浏览器
|
||||||
|
new Browser("DingTalk", "DingTalk", "AliApp\\(DingTalk\\/([\\d\\w\\.\\-]+)\\)"),
|
||||||
|
// 支付宝内置浏览器
|
||||||
|
new Browser("Alipay", "AlipayClient", "AliApp\\(AP\\/([\\d\\w\\.\\-]+)\\)"),
|
||||||
|
// 淘宝内置浏览器
|
||||||
|
new Browser("Taobao", "taobao", "AliApp\\(TB\\/([\\d\\w\\.\\-]+)\\)"),
|
||||||
|
// UC浏览器
|
||||||
|
new Browser("UCBrowser", "UC?Browser", "UC?Browser\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
// XiaoMi 浏览器
|
||||||
|
new Browser("MiuiBrowser", "MiuiBrowser|mibrowser", "MiuiBrowser\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
// 夸克浏览器
|
||||||
|
new Browser("Quark", "Quark", Other_Version),
|
||||||
|
// 联想浏览器
|
||||||
|
new Browser("Lenovo", "SLBrowser", "SLBrowser/([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("MSEdge", "Edge|Edg", "(?:edge|Edg|EdgA)\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("Chrome", "chrome", Other_Version),
|
||||||
|
new Browser("Firefox", "firefox", Other_Version),
|
||||||
|
new Browser("IEMobile", "iemobile", Other_Version),
|
||||||
|
new Browser("Android Browser", "android", "version\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("Safari", "safari", "version\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("Opera", "opera", Other_Version),
|
||||||
|
new Browser("Konqueror", "konqueror", Other_Version),
|
||||||
|
new Browser("PS3", "playstation 3", "([\\d\\w\\.\\-]+)\\)\\s*$"),
|
||||||
|
new Browser("PSP", "playstation portable", "([\\d\\w\\.\\-]+)\\)?\\s*$"),
|
||||||
|
new Browser("Lotus", "lotus.notes", "Lotus-Notes\\/([\\w.]+)"),
|
||||||
|
new Browser("Thunderbird", "thunderbird", Other_Version),
|
||||||
|
new Browser("Netscape", "netscape", Other_Version),
|
||||||
|
new Browser("Seamonkey", "seamonkey", Other_Version),
|
||||||
|
new Browser("Outlook", "microsoft.outlook", Other_Version),
|
||||||
|
new Browser("Evolution", "evolution", Other_Version),
|
||||||
|
new Browser("MSIE", "msie", "msie ([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("MSIE11", "rv:11", "rv:([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("Gabble", "Gabble", Other_Version),
|
||||||
|
new Browser("Yammer Desktop", "AdobeAir", "([\\d\\w\\.\\-]+)\\/Yammer"),
|
||||||
|
new Browser("Yammer Mobile", "Yammer[\\s]+([\\d\\w\\.\\-]+)", "Yammer[\\s]+([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("Apache HTTP Client", "Apache\\\\-HttpClient", "Apache\\-HttpClient\\/([\\d\\w\\.\\-]+)"),
|
||||||
|
new Browser("BlackBerry", "BlackBerry", "BlackBerry[\\d]+\\/([\\d\\w\\.\\-]+)")
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加自定义的浏览器类型
|
||||||
|
*
|
||||||
|
* @param name 浏览器名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
* @param versionRegex 匹配版本的正则
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
synchronized public static void addCustomBrowser(String name, String regex, String versionRegex) {
|
||||||
|
browers.add(new Browser(name, regex, versionRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern versionPattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 浏览器名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
* @param versionRegex 匹配版本的正则
|
||||||
|
*/
|
||||||
|
public Browser(String name, String regex, String versionRegex) {
|
||||||
|
super(name, regex);
|
||||||
|
if (Other_Version.equals(versionRegex)) {
|
||||||
|
versionRegex = name + versionRegex;
|
||||||
|
}
|
||||||
|
if (null != versionRegex) {
|
||||||
|
this.versionPattern = Pattern.compile(versionRegex, Pattern.CASE_INSENSITIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器版本
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 版本
|
||||||
|
*/
|
||||||
|
public String getVersion(String userAgentString) {
|
||||||
|
if (isUnknown()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReUtil.getGroup1(this.versionPattern, userAgentString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否移动浏览器
|
||||||
|
*
|
||||||
|
* @return 是否移动浏览器
|
||||||
|
*/
|
||||||
|
public boolean isMobile() {
|
||||||
|
final String name = this.getName();
|
||||||
|
return "PSP".equals(name) ||
|
||||||
|
"Yammer Mobile".equals(name) ||
|
||||||
|
"Android Browser".equals(name) ||
|
||||||
|
"IEMobile".equals(name) ||
|
||||||
|
"MicroMessenger".equals(name) ||
|
||||||
|
"miniProgram".equals(name) ||
|
||||||
|
"DingTalk".equals(name);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引擎对象
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class Engine extends UserAgentInfo {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 未知 */
|
||||||
|
public static final Engine Unknown = new Engine(NameUnknown, null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的引擎类型
|
||||||
|
*/
|
||||||
|
public static final List<Engine> engines = CollUtil.newArrayList(
|
||||||
|
new Engine("Trident", "trident"),
|
||||||
|
new Engine("Webkit", "webkit"),
|
||||||
|
new Engine("Chrome", "chrome"),
|
||||||
|
new Engine("Opera", "opera"),
|
||||||
|
new Engine("Presto", "presto"),
|
||||||
|
new Engine("Gecko", "gecko"),
|
||||||
|
new Engine("KHTML", "khtml"),
|
||||||
|
new Engine("Konqueror", "konqueror"),
|
||||||
|
new Engine("MIDP", "MIDP")
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Pattern versionPattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 引擎名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
*/
|
||||||
|
public Engine(String name, String regex) {
|
||||||
|
super(name, regex);
|
||||||
|
this.versionPattern = Pattern.compile(name + "[/\\- ]([\\d\\w.\\-]+)", Pattern.CASE_INSENSITIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取引擎版本
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 版本
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
public String getVersion(String userAgentString) {
|
||||||
|
if (isUnknown()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReUtil.getGroup1(this.versionPattern, userAgentString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统对象
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class OS extends UserAgentInfo {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未知
|
||||||
|
*/
|
||||||
|
public static final OS Unknown = new OS(NameUnknown, null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的引擎类型
|
||||||
|
*/
|
||||||
|
public static final List<OS> oses = CollUtil.newArrayList(//
|
||||||
|
new OS("Windows 10 or Windows Server 2016", "windows nt 10\\.0", "windows nt (10\\.0)"),//
|
||||||
|
new OS("Windows 8.1 or Windows Server 2012R2", "windows nt 6\\.3", "windows nt (6\\.3)"),//
|
||||||
|
new OS("Windows 8 or Windows Server 2012", "windows nt 6\\.2", "windows nt (6\\.2)"),//
|
||||||
|
new OS("Windows Vista", "windows nt 6\\.0", "windows nt (6\\.0)"), //
|
||||||
|
new OS("Windows 7 or Windows Server 2008R2", "windows nt 6\\.1", "windows nt (6\\.1)"), //
|
||||||
|
new OS("Windows 2003", "windows nt 5\\.2", "windows nt (5\\.2)"), //
|
||||||
|
new OS("Windows XP", "windows nt 5\\.1", "windows nt (5\\.1)"), //
|
||||||
|
new OS("Windows 2000", "windows nt 5\\.0", "windows nt (5\\.0)"), //
|
||||||
|
new OS("Windows Phone", "windows (ce|phone|mobile)( os)?", "windows (?:ce|phone|mobile) (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("Windows", "windows"), //
|
||||||
|
new OS("OSX", "os x (\\d+)[._](\\d+)", "os x (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("Android", "Android", "Android (\\d+([._]\\d+)*)"),//
|
||||||
|
new OS("Android", "XiaoMi|MI\\s+", "\\(X(\\d+([._]\\d+)*)"),//
|
||||||
|
new OS("Linux", "linux"), //
|
||||||
|
new OS("Wii", "wii", "wii libnup/(\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("PS3", "playstation 3", "playstation 3; (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("PSP", "playstation portable", "Portable\\); (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("iPad", "\\(iPad.*os (\\d+)[._](\\d+)", "\\(iPad.*os (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("iPhone", "\\(iPhone.*os (\\d+)[._](\\d+)", "\\(iPhone.*os (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("YPod", "iPod touch[\\s\\;]+iPhone.*os (\\d+)[._](\\d+)", "iPod touch[\\s\\;]+iPhone.*os (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("YPad", "iPad[\\s\\;]+iPhone.*os (\\d+)[._](\\d+)", "iPad[\\s\\;]+iPhone.*os (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("YPhone", "iPhone[\\s\\;]+iPhone.*os (\\d+)[._](\\d+)", "iPhone[\\s\\;]+iPhone.*os (\\d+([._]\\d+)*)"), //
|
||||||
|
new OS("Symbian", "symbian(os)?"), //
|
||||||
|
new OS("Darwin", "Darwin\\/([\\d\\w\\.\\-]+)", "Darwin\\/([\\d\\w\\.\\-]+)"), //
|
||||||
|
new OS("Adobe Air", "AdobeAir\\/([\\d\\w\\.\\-]+)", "AdobeAir\\/([\\d\\w\\.\\-]+)"), //
|
||||||
|
new OS("Java", "Java[\\s]+([\\d\\w\\.\\-]+)", "Java[\\s]+([\\d\\w\\.\\-]+)")//
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加自定义的系统类型
|
||||||
|
*
|
||||||
|
* @param name 浏览器名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
* @param versionRegex 匹配版本的正则
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
synchronized public static void addCustomOs(String name, String regex, String versionRegex) {
|
||||||
|
oses.add(new OS(name, regex, versionRegex));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern versionPattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 系统名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
*/
|
||||||
|
public OS(String name, String regex) {
|
||||||
|
this(name, regex, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 系统名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
* @param versionRegex 版本正则表达式
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
public OS(String name, String regex, String versionRegex) {
|
||||||
|
super(name, regex);
|
||||||
|
if (null != versionRegex) {
|
||||||
|
this.versionPattern = Pattern.compile(versionRegex, Pattern.CASE_INSENSITIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器版本
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 版本
|
||||||
|
*/
|
||||||
|
public String getVersion(String userAgentString) {
|
||||||
|
if (isUnknown() || null == this.versionPattern) {
|
||||||
|
// 无版本信息
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ReUtil.getGroup1(this.versionPattern, userAgentString);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台对象
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class Platform extends UserAgentInfo {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未知
|
||||||
|
*/
|
||||||
|
public static final Platform Unknown = new Platform(NameUnknown, null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iphone
|
||||||
|
*/
|
||||||
|
public static final Platform IPHONE = new Platform("iPhone", "iphone");
|
||||||
|
/**
|
||||||
|
* ipod
|
||||||
|
*/
|
||||||
|
public static final Platform IPOD = new Platform("iPod", "ipod");
|
||||||
|
/**
|
||||||
|
* ipad
|
||||||
|
*/
|
||||||
|
public static final Platform IPAD = new Platform("iPad", "ipad");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* android
|
||||||
|
*/
|
||||||
|
public static final Platform ANDROID = new Platform("Android", "android");
|
||||||
|
/**
|
||||||
|
* android
|
||||||
|
*/
|
||||||
|
public static final Platform GOOGLE_TV = new Platform("GoogleTV", "googletv");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windows Phone
|
||||||
|
*/
|
||||||
|
public static final Platform WINDOWS_PHONE = new Platform("Windows Phone", "windows (ce|phone|mobile)( os)?");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的移动平台类型
|
||||||
|
*/
|
||||||
|
public static final List<Platform> mobilePlatforms = CollUtil.newArrayList(//
|
||||||
|
WINDOWS_PHONE, //
|
||||||
|
IPAD, //
|
||||||
|
IPOD, //
|
||||||
|
IPHONE, //
|
||||||
|
new Platform("Android", "XiaoMi|MI\\s+"), //
|
||||||
|
ANDROID, //
|
||||||
|
GOOGLE_TV, //
|
||||||
|
new Platform("htcFlyer", "htc_flyer"), //
|
||||||
|
new Platform("Symbian", "symbian(os)?"), //
|
||||||
|
new Platform("Blackberry", "blackberry") //
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的桌面平台类型
|
||||||
|
*/
|
||||||
|
public static final List<Platform> desktopPlatforms = CollUtil.newArrayList(//
|
||||||
|
new Platform("Windows", "windows"), //
|
||||||
|
new Platform("Mac", "(macintosh|darwin)"), //
|
||||||
|
new Platform("Linux", "linux"), //
|
||||||
|
new Platform("Wii", "wii"), //
|
||||||
|
new Platform("Playstation", "playstation"), //
|
||||||
|
new Platform("Java", "java") //
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的平台类型
|
||||||
|
*/
|
||||||
|
public static final List<Platform> platforms;
|
||||||
|
|
||||||
|
static {
|
||||||
|
platforms = new ArrayList<>(13);
|
||||||
|
platforms.addAll(mobilePlatforms);
|
||||||
|
platforms.addAll(desktopPlatforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 平台名称
|
||||||
|
* @param regex 关键字或表达式
|
||||||
|
*/
|
||||||
|
public Platform(String name, String regex) {
|
||||||
|
super(name, regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为移动平台
|
||||||
|
*
|
||||||
|
* @return 是否为移动平台
|
||||||
|
*/
|
||||||
|
public boolean isMobile() {
|
||||||
|
return mobilePlatforms.contains(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Iphone或者iPod设备
|
||||||
|
*
|
||||||
|
* @return 是否为Iphone或者iPod设备
|
||||||
|
* @since 5.2.3
|
||||||
|
*/
|
||||||
|
public boolean isIPhoneOrIPod() {
|
||||||
|
return this.equals(IPHONE) || this.equals(IPOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Iphone或者iPod设备
|
||||||
|
*
|
||||||
|
* @return 是否为Iphone或者iPod设备
|
||||||
|
* @since 5.2.3
|
||||||
|
*/
|
||||||
|
public boolean isIPad() {
|
||||||
|
return this.equals(IPAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为IOS平台,包括IPhone、IPod、IPad
|
||||||
|
*
|
||||||
|
* @return 是否为IOS平台,包括IPhone、IPod、IPad
|
||||||
|
* @since 5.2.3
|
||||||
|
*/
|
||||||
|
public boolean isIos() {
|
||||||
|
return isIPhoneOrIPod() || isIPad();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Android平台,包括Android和Google TV
|
||||||
|
*
|
||||||
|
* @return 是否为Android平台,包括Android和Google TV
|
||||||
|
* @since 5.2.3
|
||||||
|
*/
|
||||||
|
public boolean isAndroid() {
|
||||||
|
return this.equals(ANDROID) || this.equals(GOOGLE_TV);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-Agent信息对象
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class UserAgent implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为移动平台
|
||||||
|
*/
|
||||||
|
private boolean mobile;
|
||||||
|
/**
|
||||||
|
* 浏览器类型
|
||||||
|
*/
|
||||||
|
private aiyh.utils.tool.cn.hutool.http.useragent.Browser browser;
|
||||||
|
/**
|
||||||
|
* 浏览器版本
|
||||||
|
*/
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平台类型
|
||||||
|
*/
|
||||||
|
private aiyh.utils.tool.cn.hutool.http.useragent.Platform platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统类型
|
||||||
|
*/
|
||||||
|
private aiyh.utils.tool.cn.hutool.http.useragent.OS os;
|
||||||
|
/**
|
||||||
|
* 系统版本
|
||||||
|
*/
|
||||||
|
private String osVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引擎类型
|
||||||
|
*/
|
||||||
|
private aiyh.utils.tool.cn.hutool.http.useragent.Engine engine;
|
||||||
|
/**
|
||||||
|
* 引擎版本
|
||||||
|
*/
|
||||||
|
private String engineVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为移动平台
|
||||||
|
*
|
||||||
|
* @return 是否为移动平台
|
||||||
|
*/
|
||||||
|
public boolean isMobile() {
|
||||||
|
return mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否为移动平台
|
||||||
|
*
|
||||||
|
* @param mobile 是否为移动平台
|
||||||
|
*/
|
||||||
|
public void setMobile(boolean mobile) {
|
||||||
|
this.mobile = mobile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器类型
|
||||||
|
*
|
||||||
|
* @return 浏览器类型
|
||||||
|
*/
|
||||||
|
public aiyh.utils.tool.cn.hutool.http.useragent.Browser getBrowser() {
|
||||||
|
return browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置浏览器类型
|
||||||
|
*
|
||||||
|
* @param browser 浏览器类型
|
||||||
|
*/
|
||||||
|
public void setBrowser(Browser browser) {
|
||||||
|
this.browser = browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取平台类型
|
||||||
|
*
|
||||||
|
* @return 平台类型
|
||||||
|
*/
|
||||||
|
public aiyh.utils.tool.cn.hutool.http.useragent.Platform getPlatform() {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置平台类型
|
||||||
|
*
|
||||||
|
* @param platform 平台类型
|
||||||
|
*/
|
||||||
|
public void setPlatform(Platform platform) {
|
||||||
|
this.platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统类型
|
||||||
|
*
|
||||||
|
* @return 系统类型
|
||||||
|
*/
|
||||||
|
public aiyh.utils.tool.cn.hutool.http.useragent.OS getOs() {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置系统类型
|
||||||
|
*
|
||||||
|
* @param os 系统类型
|
||||||
|
*/
|
||||||
|
public void setOs(OS os) {
|
||||||
|
this.os = os;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统版本
|
||||||
|
*
|
||||||
|
* @return 系统版本
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
public String getOsVersion() {
|
||||||
|
return this.osVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置系统版本
|
||||||
|
*
|
||||||
|
* @param osVersion 系统版本
|
||||||
|
* @since 5.7.4
|
||||||
|
*/
|
||||||
|
public void setOsVersion(String osVersion) {
|
||||||
|
this.osVersion = osVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取引擎类型
|
||||||
|
*
|
||||||
|
* @return 引擎类型
|
||||||
|
*/
|
||||||
|
public aiyh.utils.tool.cn.hutool.http.useragent.Engine getEngine() {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置引擎类型
|
||||||
|
*
|
||||||
|
* @param engine 引擎类型
|
||||||
|
*/
|
||||||
|
public void setEngine(Engine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取浏览器版本
|
||||||
|
*
|
||||||
|
* @return 浏览器版本
|
||||||
|
*/
|
||||||
|
public String getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置浏览器版本
|
||||||
|
*
|
||||||
|
* @param version 浏览器版本
|
||||||
|
*/
|
||||||
|
public void setVersion(String version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取引擎版本
|
||||||
|
*
|
||||||
|
* @return 引擎版本
|
||||||
|
*/
|
||||||
|
public String getEngineVersion() {
|
||||||
|
return engineVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置引擎版本
|
||||||
|
*
|
||||||
|
* @param engineVersion 引擎版本
|
||||||
|
*/
|
||||||
|
public void setEngineVersion(String engineVersion) {
|
||||||
|
this.engineVersion = engineVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ReUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-agent信息
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class UserAgentInfo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未知类型
|
||||||
|
*/
|
||||||
|
public static final String NameUnknown = "Unknown";
|
||||||
|
|
||||||
|
/** 信息名称 */
|
||||||
|
private final String name;
|
||||||
|
/** 信息匹配模式 */
|
||||||
|
private final Pattern pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 名字
|
||||||
|
* @param regex 表达式
|
||||||
|
*/
|
||||||
|
public UserAgentInfo(String name, String regex) {
|
||||||
|
this(name, (null == regex) ? null : Pattern.compile(regex, Pattern.CASE_INSENSITIVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param name 名字
|
||||||
|
* @param pattern 匹配模式
|
||||||
|
*/
|
||||||
|
public UserAgentInfo(String name, Pattern pattern) {
|
||||||
|
this.name = name;
|
||||||
|
this.pattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取信息名称
|
||||||
|
*
|
||||||
|
* @return 信息名称
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取匹配模式
|
||||||
|
*
|
||||||
|
* @return 匹配模式
|
||||||
|
*/
|
||||||
|
public Pattern getPattern() {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定内容中是否包含匹配此信息的内容
|
||||||
|
*
|
||||||
|
* @param content User-Agent字符串
|
||||||
|
* @return 是否包含匹配此信息的内容
|
||||||
|
*/
|
||||||
|
public boolean isMatch(String content) {
|
||||||
|
return ReUtil.contains(this.pattern, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为Unknown
|
||||||
|
*
|
||||||
|
* @return 是否为Unknown
|
||||||
|
*/
|
||||||
|
public boolean isUnknown() {
|
||||||
|
return NameUnknown.equals(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final UserAgentInfo other = (UserAgentInfo) obj;
|
||||||
|
if (name == null) {
|
||||||
|
return other.name == null;
|
||||||
|
} else return name.equals(other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-Agent解析器
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.2.1
|
||||||
|
*/
|
||||||
|
public class UserAgentParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析User-Agent
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return {@link UserAgent}
|
||||||
|
*/
|
||||||
|
public static UserAgent parse(String userAgentString) {
|
||||||
|
if (StrUtil.isBlank(userAgentString)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final UserAgent userAgent = new UserAgent();
|
||||||
|
|
||||||
|
// 浏览器
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.useragent.Browser browser = parseBrowser(userAgentString);
|
||||||
|
userAgent.setBrowser(browser);
|
||||||
|
userAgent.setVersion(browser.getVersion(userAgentString));
|
||||||
|
|
||||||
|
// 浏览器引擎
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.useragent.Engine engine = parseEngine(userAgentString);
|
||||||
|
userAgent.setEngine(engine);
|
||||||
|
userAgent.setEngineVersion(engine.getVersion(userAgentString));
|
||||||
|
|
||||||
|
// 操作系统
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.useragent.OS os = parseOS(userAgentString);
|
||||||
|
userAgent.setOs(os);
|
||||||
|
userAgent.setOsVersion(os.getVersion(userAgentString));
|
||||||
|
|
||||||
|
// 平台
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.useragent.Platform platform = parsePlatform(userAgentString);
|
||||||
|
userAgent.setPlatform(platform);
|
||||||
|
userAgent.setMobile(platform.isMobile() || browser.isMobile());
|
||||||
|
|
||||||
|
|
||||||
|
return userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析浏览器类型
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 浏览器类型
|
||||||
|
*/
|
||||||
|
private static aiyh.utils.tool.cn.hutool.http.useragent.Browser parseBrowser(String userAgentString) {
|
||||||
|
for (aiyh.utils.tool.cn.hutool.http.useragent.Browser browser : aiyh.utils.tool.cn.hutool.http.useragent.Browser.browers) {
|
||||||
|
if (browser.isMatch(userAgentString)) {
|
||||||
|
return browser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Browser.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析引擎类型
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 引擎类型
|
||||||
|
*/
|
||||||
|
private static aiyh.utils.tool.cn.hutool.http.useragent.Engine parseEngine(String userAgentString) {
|
||||||
|
for (aiyh.utils.tool.cn.hutool.http.useragent.Engine engine : aiyh.utils.tool.cn.hutool.http.useragent.Engine.engines) {
|
||||||
|
if (engine.isMatch(userAgentString)) {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Engine.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析系统类型
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 系统类型
|
||||||
|
*/
|
||||||
|
private static aiyh.utils.tool.cn.hutool.http.useragent.OS parseOS(String userAgentString) {
|
||||||
|
for (aiyh.utils.tool.cn.hutool.http.useragent.OS os : aiyh.utils.tool.cn.hutool.http.useragent.OS.oses) {
|
||||||
|
if (os.isMatch(userAgentString)) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OS.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析平台类型
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return 平台类型
|
||||||
|
*/
|
||||||
|
private static aiyh.utils.tool.cn.hutool.http.useragent.Platform parsePlatform(String userAgentString) {
|
||||||
|
for (aiyh.utils.tool.cn.hutool.http.useragent.Platform platform : aiyh.utils.tool.cn.hutool.http.useragent.Platform.platforms) {
|
||||||
|
if (platform.isMatch(userAgentString)) {
|
||||||
|
return platform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Platform.Unknown;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User-Agent工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public class UserAgentUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析User-Agent
|
||||||
|
*
|
||||||
|
* @param userAgentString User-Agent字符串
|
||||||
|
* @return {@link UserAgent}
|
||||||
|
*/
|
||||||
|
public static UserAgent parse(String userAgentString) {
|
||||||
|
return UserAgentParser.parse(userAgentString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* User-Agent解析
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.useragent;
|
|
@ -0,0 +1,654 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.webservice;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.collection.CollUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.io.IoUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.map.MapUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.ObjectUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.XmlUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpBase;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpGlobalConfig;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpRequest;
|
||||||
|
import aiyh.utils.tool.cn.hutool.http.HttpResponse;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
import javax.xml.soap.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOAP客户端
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 此对象用于构建一个SOAP消息,并通过HTTP接口发出消息内容。
|
||||||
|
* SOAP消息本质上是一个XML文本,可以通过调用{@link #getMsgStr(boolean)} 方法获取消息体
|
||||||
|
* <p>
|
||||||
|
* 使用方法:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* SoapClient client = SoapClient.create(url)
|
||||||
|
* .setMethod(methodName, namespaceURI)
|
||||||
|
* .setCharset(CharsetUtil.CHARSET_GBK)
|
||||||
|
* .setParam(param1, "XXX");
|
||||||
|
*
|
||||||
|
* String response = client.send(true);
|
||||||
|
*
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.5.4
|
||||||
|
*/
|
||||||
|
public class SoapClient extends HttpBase<SoapClient> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XML消息体的Content-Type
|
||||||
|
* soap1.1 : text/xml
|
||||||
|
* soap1.2 : application/soap+xml
|
||||||
|
* soap1.1与soap1.2区别: https://www.cnblogs.com/qlqwjy/p/7577147.html
|
||||||
|
*/
|
||||||
|
private static final String CONTENT_TYPE_SOAP11_TEXT_XML = "text/xml;charset=";
|
||||||
|
private static final String CONTENT_TYPE_SOAP12_SOAP_XML = "application/soap+xml;charset=";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求的URL地址
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认连接超时
|
||||||
|
*/
|
||||||
|
private int connectionTimeout = aiyh.utils.tool.cn.hutool.http.HttpGlobalConfig.getTimeout();
|
||||||
|
/**
|
||||||
|
* 默认读取超时
|
||||||
|
*/
|
||||||
|
private int readTimeout = HttpGlobalConfig.getTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息工厂,用于创建消息
|
||||||
|
*/
|
||||||
|
private MessageFactory factory;
|
||||||
|
/**
|
||||||
|
* SOAP消息
|
||||||
|
*/
|
||||||
|
private SOAPMessage message;
|
||||||
|
/**
|
||||||
|
* 消息方法节点
|
||||||
|
*/
|
||||||
|
private SOAPBodyElement methodEle;
|
||||||
|
/**
|
||||||
|
* 应用于方法上的命名空间URI
|
||||||
|
*/
|
||||||
|
private final String namespaceURI;
|
||||||
|
/**
|
||||||
|
* Soap协议
|
||||||
|
* soap1.1 : text/xml
|
||||||
|
* soap1.2 : application/soap+xml
|
||||||
|
*/
|
||||||
|
private final SoapProtocol protocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端,默认使用soap1.1版本协议
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public static SoapClient create(String url) {
|
||||||
|
return new SoapClient(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议,见{@link SoapProtocol}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public static SoapClient create(String url, SoapProtocol protocol) {
|
||||||
|
return new SoapClient(url, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议,见{@link SoapProtocol}
|
||||||
|
* @param namespaceURI 方法上的命名空间URI
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public static SoapClient create(String url, SoapProtocol protocol, String namespaceURI) {
|
||||||
|
return new SoapClient(url, protocol, namespaceURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,默认使用soap1.1版本协议
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
*/
|
||||||
|
public SoapClient(String url) {
|
||||||
|
this(url, SoapProtocol.SOAP_1_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议版本,见{@link SoapProtocol}
|
||||||
|
*/
|
||||||
|
public SoapClient(String url, SoapProtocol protocol) {
|
||||||
|
this(url, protocol, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议版本,见{@link SoapProtocol}
|
||||||
|
* @param namespaceURI 方法上的命名空间URI
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient(String url, SoapProtocol protocol, String namespaceURI) {
|
||||||
|
this.url = url;
|
||||||
|
this.namespaceURI = namespaceURI;
|
||||||
|
this.protocol = protocol;
|
||||||
|
init(protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*
|
||||||
|
* @param protocol 协议版本枚举,见{@link SoapProtocol}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient init(SoapProtocol protocol) {
|
||||||
|
// 创建消息工厂
|
||||||
|
try {
|
||||||
|
this.factory = MessageFactory.newInstance(protocol.getValue());
|
||||||
|
// 根据消息工厂创建SoapMessage
|
||||||
|
this.message = factory.createMessage();
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置SOAP客户端,用于客户端复用
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 重置后需调用serMethod方法重新指定请求方法,并调用setParam方法重新定义参数
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
* @since 4.6.7
|
||||||
|
*/
|
||||||
|
public SoapClient reset() {
|
||||||
|
try {
|
||||||
|
this.message = factory.createMessage();
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
this.methodEle = null;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置编码
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return this
|
||||||
|
* @see #charset(Charset)
|
||||||
|
*/
|
||||||
|
public SoapClient setCharset(Charset charset) {
|
||||||
|
return this.charset(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoapClient charset(Charset charset) {
|
||||||
|
super.charset(charset);
|
||||||
|
try {
|
||||||
|
this.message.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, this.charset());
|
||||||
|
this.message.setProperty(SOAPMessage.WRITE_XML_DECLARATION, "true");
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Webservice请求地址
|
||||||
|
*
|
||||||
|
* @param url Webservice请求地址
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加SOAP头信息,方法返回{@link SOAPHeaderElement}可以设置具体属性和子节点
|
||||||
|
*
|
||||||
|
* @param name 头信息标签名
|
||||||
|
* @param actorURI 中间的消息接收者
|
||||||
|
* @param roleUri Role的URI
|
||||||
|
* @param mustUnderstand 标题项对于要对其进行处理的接收者来说是强制的还是可选的
|
||||||
|
* @param relay relay属性
|
||||||
|
* @return {@link SOAPHeaderElement}
|
||||||
|
* @since 5.4.4
|
||||||
|
*/
|
||||||
|
public SOAPHeaderElement addSOAPHeader(QName name, String actorURI, String roleUri, Boolean mustUnderstand, Boolean relay) {
|
||||||
|
final SOAPHeaderElement ele = addSOAPHeader(name);
|
||||||
|
try {
|
||||||
|
if (StrUtil.isNotBlank(roleUri)) {
|
||||||
|
ele.setRole(roleUri);
|
||||||
|
}
|
||||||
|
if (null != relay) {
|
||||||
|
ele.setRelay(relay);
|
||||||
|
}
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StrUtil.isNotBlank(actorURI)) {
|
||||||
|
ele.setActor(actorURI);
|
||||||
|
}
|
||||||
|
if (null != mustUnderstand) {
|
||||||
|
ele.setMustUnderstand(mustUnderstand);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加SOAP头信息,方法返回{@link SOAPHeaderElement}可以设置具体属性和子节点
|
||||||
|
*
|
||||||
|
* @param localName 头节点名称
|
||||||
|
* @return {@link SOAPHeaderElement}
|
||||||
|
* @since 5.4.7
|
||||||
|
*/
|
||||||
|
public SOAPHeaderElement addSOAPHeader(String localName) {
|
||||||
|
return addSOAPHeader(new QName(localName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加SOAP头信息,方法返回{@link SOAPHeaderElement}可以设置具体属性和子节点
|
||||||
|
*
|
||||||
|
* @param localName 头节点名称
|
||||||
|
* @param value 头节点的值
|
||||||
|
* @return {@link SOAPHeaderElement}
|
||||||
|
* @since 5.4.7
|
||||||
|
*/
|
||||||
|
public SOAPHeaderElement addSOAPHeader(String localName, String value) {
|
||||||
|
final SOAPHeaderElement soapHeaderElement = addSOAPHeader(localName);
|
||||||
|
soapHeaderElement.setTextContent(value);
|
||||||
|
return soapHeaderElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加SOAP头信息,方法返回{@link SOAPHeaderElement}可以设置具体属性和子节点
|
||||||
|
*
|
||||||
|
* @param name 头节点名称
|
||||||
|
* @return {@link SOAPHeaderElement}
|
||||||
|
* @since 5.4.4
|
||||||
|
*/
|
||||||
|
public SOAPHeaderElement addSOAPHeader(QName name) {
|
||||||
|
SOAPHeaderElement ele;
|
||||||
|
try {
|
||||||
|
ele = this.message.getSOAPHeader().addHeaderElement(name);
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法
|
||||||
|
*
|
||||||
|
* @param name 方法名及其命名空间
|
||||||
|
* @param params 参数
|
||||||
|
* @param useMethodPrefix 是否使用方法的命名空间前缀
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setMethod(Name name, Map<String, Object> params, boolean useMethodPrefix) {
|
||||||
|
return setMethod(new QName(name.getURI(), name.getLocalName(), name.getPrefix()), params, useMethodPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法
|
||||||
|
*
|
||||||
|
* @param name 方法名及其命名空间
|
||||||
|
* @param params 参数
|
||||||
|
* @param useMethodPrefix 是否使用方法的命名空间前缀
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setMethod(QName name, Map<String, Object> params, boolean useMethodPrefix) {
|
||||||
|
setMethod(name);
|
||||||
|
final String prefix = useMethodPrefix ? name.getPrefix() : null;
|
||||||
|
final SOAPBodyElement methodEle = this.methodEle;
|
||||||
|
for (Entry<String, Object> entry : MapUtil.wrap(params)) {
|
||||||
|
setParam(methodEle, entry.getKey(), entry.getValue(), prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法<br>
|
||||||
|
* 方法名自动识别前缀,前缀和方法名使用“:”分隔<br>
|
||||||
|
* 当识别到前缀后,自动添加xmlns属性,关联到默认的namespaceURI
|
||||||
|
*
|
||||||
|
* @param methodName 方法名
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setMethod(String methodName) {
|
||||||
|
return setMethod(methodName, ObjectUtil.defaultIfNull(this.namespaceURI, XMLConstants.NULL_NS_URI));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法<br>
|
||||||
|
* 方法名自动识别前缀,前缀和方法名使用“:”分隔<br>
|
||||||
|
* 当识别到前缀后,自动添加xmlns属性,关联到传入的namespaceURI
|
||||||
|
*
|
||||||
|
* @param methodName 方法名(可有前缀也可无)
|
||||||
|
* @param namespaceURI 命名空间URI
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setMethod(String methodName, String namespaceURI) {
|
||||||
|
final List<String> methodNameList = StrUtil.split(methodName, ':');
|
||||||
|
final QName qName;
|
||||||
|
if (2 == methodNameList.size()) {
|
||||||
|
qName = new QName(namespaceURI, methodNameList.get(1), methodNameList.get(0));
|
||||||
|
} else {
|
||||||
|
qName = new QName(namespaceURI, methodName);
|
||||||
|
}
|
||||||
|
return setMethod(qName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求方法
|
||||||
|
*
|
||||||
|
* @param name 方法名及其命名空间
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setMethod(QName name) {
|
||||||
|
try {
|
||||||
|
this.methodEle = this.message.getSOAPBody().addBodyElement(name);
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置方法参数,使用方法的前缀
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @param value 参数值,可以是字符串或Map或{@link SOAPElement}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setParam(String name, Object value) {
|
||||||
|
return setParam(name, value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置方法参数
|
||||||
|
*
|
||||||
|
* @param name 参数名
|
||||||
|
* @param value 参数值,可以是字符串或Map或{@link SOAPElement}
|
||||||
|
* @param useMethodPrefix 是否使用方法的命名空间前缀
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public SoapClient setParam(String name, Object value, boolean useMethodPrefix) {
|
||||||
|
setParam(this.methodEle, name, value, useMethodPrefix ? this.methodEle.getPrefix() : null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量设置参数,使用方法的前缀
|
||||||
|
*
|
||||||
|
* @param params 参数列表
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient setParams(Map<String, Object> params) {
|
||||||
|
return setParams(params, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量设置参数
|
||||||
|
*
|
||||||
|
* @param params 参数列表
|
||||||
|
* @param useMethodPrefix 是否使用方法的命名空间前缀
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient setParams(Map<String, Object> params, boolean useMethodPrefix) {
|
||||||
|
for (Entry<String, Object> entry : MapUtil.wrap(params)) {
|
||||||
|
setParam(entry.getKey(), entry.getValue(), useMethodPrefix);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取方法节点<br>
|
||||||
|
* 用于创建子节点等操作
|
||||||
|
*
|
||||||
|
* @return {@link SOAPBodyElement}
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SOAPBodyElement getMethodEle() {
|
||||||
|
return this.methodEle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SOAP消息对象 {@link SOAPMessage}
|
||||||
|
*
|
||||||
|
* @return {@link SOAPMessage}
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SOAPMessage getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SOAP请求消息
|
||||||
|
*
|
||||||
|
* @param pretty 是否格式化
|
||||||
|
* @return 消息字符串
|
||||||
|
*/
|
||||||
|
public String getMsgStr(boolean pretty) {
|
||||||
|
return SoapUtil.toString(this.message, pretty, this.charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将SOAP消息的XML内容输出到流
|
||||||
|
*
|
||||||
|
* @param out 输出流
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient write(OutputStream out) {
|
||||||
|
try {
|
||||||
|
this.message.writeTo(out);
|
||||||
|
} catch (SOAPException | IOException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置超时,单位:毫秒<br>
|
||||||
|
* 超时包括:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 1. 连接超时
|
||||||
|
* 2. 读取响应超时
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
* @see #setConnectionTimeout(int)
|
||||||
|
* @see #setReadTimeout(int)
|
||||||
|
*/
|
||||||
|
public SoapClient timeout(int milliseconds) {
|
||||||
|
setConnectionTimeout(milliseconds);
|
||||||
|
setReadTimeout(milliseconds);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接超时,单位:毫秒
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient setConnectionTimeout(int milliseconds) {
|
||||||
|
this.connectionTimeout = milliseconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置连接超时,单位:毫秒
|
||||||
|
*
|
||||||
|
* @param milliseconds 超时毫秒数
|
||||||
|
* @return this
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public SoapClient setReadTimeout(int milliseconds) {
|
||||||
|
this.readTimeout = milliseconds;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行Webservice请求,即发送SOAP内容
|
||||||
|
*
|
||||||
|
* @return 返回结果
|
||||||
|
*/
|
||||||
|
public SOAPMessage sendForMessage() {
|
||||||
|
final aiyh.utils.tool.cn.hutool.http.HttpResponse res = sendForResponse();
|
||||||
|
final MimeHeaders headers = new MimeHeaders();
|
||||||
|
for (Entry<String, List<String>> entry : res.headers().entrySet()) {
|
||||||
|
if (StrUtil.isNotEmpty(entry.getKey())) {
|
||||||
|
headers.setHeader(entry.getKey(), CollUtil.get(entry.getValue(), 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return this.factory.createMessage(headers, res.bodyStream());
|
||||||
|
} catch (IOException | SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
IoUtil.close(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行Webservice请求,即发送SOAP内容
|
||||||
|
*
|
||||||
|
* @return 返回结果
|
||||||
|
*/
|
||||||
|
public String send() {
|
||||||
|
return send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行Webservice请求,即发送SOAP内容
|
||||||
|
*
|
||||||
|
* @param pretty 是否格式化
|
||||||
|
* @return 返回结果
|
||||||
|
*/
|
||||||
|
public String send(boolean pretty) {
|
||||||
|
final String body = sendForResponse().body();
|
||||||
|
return pretty ? XmlUtil.format(body) : body;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送请求,获取异步响应
|
||||||
|
*
|
||||||
|
* @return 响应对象
|
||||||
|
*/
|
||||||
|
public HttpResponse sendForResponse() {
|
||||||
|
return HttpRequest.post(this.url)//
|
||||||
|
.setFollowRedirects(true)//
|
||||||
|
.setConnectionTimeout(this.connectionTimeout)
|
||||||
|
.setReadTimeout(this.readTimeout)
|
||||||
|
.contentType(getXmlContentType())//
|
||||||
|
.header(this.headers())
|
||||||
|
.body(getMsgStr(false))//
|
||||||
|
.executeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求的Content-Type,附加编码信息
|
||||||
|
*
|
||||||
|
* @return 请求的Content-Type
|
||||||
|
*/
|
||||||
|
private String getXmlContentType() {
|
||||||
|
switch (this.protocol) {
|
||||||
|
case SOAP_1_1:
|
||||||
|
return CONTENT_TYPE_SOAP11_TEXT_XML.concat(this.charset.toString());
|
||||||
|
case SOAP_1_2:
|
||||||
|
return CONTENT_TYPE_SOAP12_SOAP_XML.concat(this.charset.toString());
|
||||||
|
default:
|
||||||
|
throw new SoapRuntimeException("Unsupported protocol: {}", this.protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置方法参数
|
||||||
|
*
|
||||||
|
* @param ele 方法节点
|
||||||
|
* @param name 参数名
|
||||||
|
* @param value 参数值
|
||||||
|
* @param prefix 命名空间前缀, {@code null}表示不使用前缀
|
||||||
|
* @return {@link SOAPElement}子节点
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static SOAPElement setParam(SOAPElement ele, String name, Object value, String prefix) {
|
||||||
|
final SOAPElement childEle;
|
||||||
|
try {
|
||||||
|
if (StrUtil.isNotBlank(prefix)) {
|
||||||
|
childEle = ele.addChildElement(name, prefix);
|
||||||
|
} else {
|
||||||
|
childEle = ele.addChildElement(name);
|
||||||
|
}
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null != value) {
|
||||||
|
if (value instanceof SOAPElement) {
|
||||||
|
// 单个子节点
|
||||||
|
try {
|
||||||
|
ele.addChildElement((SOAPElement) value);
|
||||||
|
} catch (SOAPException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (value instanceof Map) {
|
||||||
|
// 多个字节点
|
||||||
|
Entry entry;
|
||||||
|
for (Object obj : ((Map) value).entrySet()) {
|
||||||
|
entry = (Entry) obj;
|
||||||
|
setParam(childEle, entry.getKey().toString(), entry.getValue(), prefix);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单个值
|
||||||
|
childEle.setValue(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return childEle;
|
||||||
|
}
|
||||||
|
// -------------------------------------------------------------------------------------------------------- Private method end
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.webservice;
|
||||||
|
|
||||||
|
import javax.xml.soap.SOAPConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOAP协议版本枚举
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
public enum SoapProtocol {
|
||||||
|
/** SOAP 1.1协议 */
|
||||||
|
SOAP_1_1(SOAPConstants.SOAP_1_1_PROTOCOL),
|
||||||
|
/** SOAP 1.2协议 */
|
||||||
|
SOAP_1_2(SOAPConstants.SOAP_1_2_PROTOCOL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param value {@link SOAPConstants} 中的协议版本值
|
||||||
|
*/
|
||||||
|
SoapProtocol(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取版本值信息
|
||||||
|
*
|
||||||
|
* @return 版本值信息
|
||||||
|
*/
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.webservice;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOAP异常
|
||||||
|
*
|
||||||
|
* @author xiaoleilu
|
||||||
|
*/
|
||||||
|
public class SoapRuntimeException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 8247610319171014183L;
|
||||||
|
|
||||||
|
public SoapRuntimeException(Throwable e) {
|
||||||
|
super(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoapRuntimeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoapRuntimeException(String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoapRuntimeException(String message, Throwable throwable) {
|
||||||
|
super(message, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoapRuntimeException(Throwable throwable, String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params), throwable);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.webservice;
|
||||||
|
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.exceptions.UtilException;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.CharsetUtil;
|
||||||
|
import aiyh.utils.tool.cn.hutool.core.util.XmlUtil;
|
||||||
|
|
||||||
|
import javax.xml.soap.SOAPException;
|
||||||
|
import javax.xml.soap.SOAPMessage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SOAP相关工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public class SoapUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端,默认使用soap1.1版本协议
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.webservice.SoapClient}
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.webservice.SoapClient createClient(String url) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.webservice.SoapClient.create(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议,见{@link SoapProtocol}
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.webservice.SoapClient}
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.webservice.SoapClient createClient(String url, SoapProtocol protocol) {
|
||||||
|
return aiyh.utils.tool.cn.hutool.http.webservice.SoapClient.create(url, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建SOAP客户端
|
||||||
|
*
|
||||||
|
* @param url WS的URL地址
|
||||||
|
* @param protocol 协议,见{@link SoapProtocol}
|
||||||
|
* @param namespaceURI 方法上的命名空间URI
|
||||||
|
* @return {@link aiyh.utils.tool.cn.hutool.http.webservice.SoapClient}
|
||||||
|
* @since 4.5.6
|
||||||
|
*/
|
||||||
|
public static aiyh.utils.tool.cn.hutool.http.webservice.SoapClient createClient(String url, SoapProtocol protocol, String namespaceURI) {
|
||||||
|
return SoapClient.create(url, protocol, namespaceURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SOAPMessage} 转为字符串
|
||||||
|
*
|
||||||
|
* @param message SOAP消息对象
|
||||||
|
* @param pretty 是否格式化
|
||||||
|
* @return SOAP XML字符串
|
||||||
|
*/
|
||||||
|
public static String toString(SOAPMessage message, boolean pretty) {
|
||||||
|
return toString(message, pretty, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SOAPMessage} 转为字符串
|
||||||
|
*
|
||||||
|
* @param message SOAP消息对象
|
||||||
|
* @param pretty 是否格式化
|
||||||
|
* @param charset 编码
|
||||||
|
* @return SOAP XML字符串
|
||||||
|
* @since 4.5.7
|
||||||
|
*/
|
||||||
|
public static String toString(SOAPMessage message, boolean pretty, Charset charset) {
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
message.writeTo(out);
|
||||||
|
} catch (SOAPException | IOException e) {
|
||||||
|
throw new SoapRuntimeException(e);
|
||||||
|
}
|
||||||
|
String messageToString;
|
||||||
|
try {
|
||||||
|
messageToString = out.toString(charset.toString());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new UtilException(e);
|
||||||
|
}
|
||||||
|
return pretty ? XmlUtil.format(messageToString) : messageToString;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/**
|
||||||
|
* Webservice客户端封装实现
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
*/
|
||||||
|
package aiyh.utils.tool.cn.hutool.http.webservice;
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2001-2018 泛微软件.
|
||||||
|
* 泛微协同商务系统,版权所有.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.api.chaoyang.he.browser.service.impl;
|
||||||
|
|
||||||
|
import com.api.browser.bean.ListHeadBean;
|
||||||
|
import com.api.browser.bean.SearchConditionItem;
|
||||||
|
import com.api.browser.bean.SplitTableBean;
|
||||||
|
import com.api.browser.bean.SplitTableColBean;
|
||||||
|
import com.api.browser.service.BrowserService;
|
||||||
|
import com.api.browser.util.*;
|
||||||
|
import com.cloudstore.dev.api.bean.SplitMobileDataBean;
|
||||||
|
import com.cloudstore.dev.api.bean.SplitMobileTemplateBean;
|
||||||
|
import com.cloudstore.dev.api.util.Util_MobileData;
|
||||||
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
import weaver.fna.e9.controller.base.FnaInvoiceLedgerController;
|
||||||
|
import weaver.fna.invoice.Constants;
|
||||||
|
import weaver.general.Util;
|
||||||
|
import weaver.systeminfo.SystemEnv;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhangwj
|
||||||
|
* @Description:多发票浏览按钮
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class MultiInvoiceBrowserService extends BrowserService {
|
||||||
|
|
||||||
|
//移动端返回数据格式的json配置
|
||||||
|
public static final String JSON_CONFIG="[" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"col1\"," +
|
||||||
|
" \"configs\": [" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"col1_row1\"," +
|
||||||
|
" \"configs\": [" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"invoiceNumber\"" +
|
||||||
|
" }," +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"invoiceCode\"," +
|
||||||
|
" \"style\": {" +
|
||||||
|
" \"float\": \"right\"" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" ]" +
|
||||||
|
" }," +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"col1_row2\"," +
|
||||||
|
" \"configs\": [" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"taxIncludedPrice\"" +
|
||||||
|
" }," +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"invoiceTypeName\"," +
|
||||||
|
" \"style\": {" +
|
||||||
|
" \"float\": \"right\"" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" ]" +
|
||||||
|
" }" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"col1_row3\"," +
|
||||||
|
" \"configs\": [" +
|
||||||
|
" {" +
|
||||||
|
" \"key\": \"billingdate\"" +
|
||||||
|
" }," +
|
||||||
|
" ]" +
|
||||||
|
" }" +
|
||||||
|
" ]" +
|
||||||
|
" }" +
|
||||||
|
"]";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多发票浏览
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getBrowserData(Map<String, Object> params) throws Exception {
|
||||||
|
Map<String, Object> apidatas = new HashMap<String, Object>();
|
||||||
|
if(user == null){
|
||||||
|
apidatas.put(BrowserConstant.BROWSER_RESULT_DATA, null);
|
||||||
|
return apidatas;
|
||||||
|
}
|
||||||
|
String advQry_kprq1 = Util.null2String(params.get("createdatestart")).trim();
|
||||||
|
String advQry_kprq2 = Util.null2String(params.get("createdateend")).trim();
|
||||||
|
String invoiceNumber = Util.null2String(params.get("invoiceNumber"));
|
||||||
|
String seller = Util.null2String(params.get("seller"));
|
||||||
|
|
||||||
|
//发票代码,发票类型,开票日期,金额
|
||||||
|
FnaInvoiceLedgerController fnaInvoiceLedgerController = FnaInvoiceLedgerController.getInstance();
|
||||||
|
String sqlAppend = fnaInvoiceLedgerController.getCaseWhenSql4InvoiceTypeList("invoiceTypeName", "a.invoiceType", user.getLanguage())+" ";
|
||||||
|
//设置好搜索条件
|
||||||
|
String backFields =" a.*, "+
|
||||||
|
fnaInvoiceLedgerController.getCaseWhenSql4InvoiceTypeList("invoiceTypeName", "a.invoiceType", user.getLanguage())+" ";
|
||||||
|
String fromSql = " from FnaInvoiceLedger a ";
|
||||||
|
//验票为真的发票才能选择的到
|
||||||
|
StringBuffer sqlWhere = new StringBuffer(" where 1=1 and (checkStatus = 1 or checkStatus = 2) ");
|
||||||
|
if(!"".equals(advQry_kprq2)){
|
||||||
|
sqlWhere.append(" and a.billingDate <= '").append(StringEscapeUtils.escapeSql(advQry_kprq2)).append("' ");
|
||||||
|
}
|
||||||
|
if(!"".equals(advQry_kprq1)){
|
||||||
|
sqlWhere.append(" and a.billingDate >= '").append(StringEscapeUtils.escapeSql(advQry_kprq1)).append("' ");
|
||||||
|
}
|
||||||
|
if(!"".equals(invoiceNumber)){
|
||||||
|
sqlWhere.append(" and a.invoiceNumber like '%").append(StringEscapeUtils.escapeSql(invoiceNumber.trim())).append("%'");
|
||||||
|
}
|
||||||
|
if(!"".equals(seller)){
|
||||||
|
sqlWhere.append(" and a.seller like '%").append(StringEscapeUtils.escapeSql(seller.trim())).append("%'");
|
||||||
|
}
|
||||||
|
sqlWhere.append(" and (a.userid_new = ").append(user.getUID()).append(" or a.id in ( select invoiceId from fnaInvoiceSharer where sharer = ").append(user.getUID()).append(") ").append(") ");
|
||||||
|
sqlWhere.append(" and a.status = '0' ");
|
||||||
|
//AP航信过滤浏览按钮中的数据-hcy
|
||||||
|
// sqlWhere.append(" and hxjksflr is null ");
|
||||||
|
String orderBy=" a.id desc ";
|
||||||
|
|
||||||
|
//writeLog("select "+backFields+" "+fromSql+" "+sqlWhere);
|
||||||
|
List<SplitTableColBean> cols = new ArrayList<SplitTableColBean>();
|
||||||
|
cols.add(new SplitTableColBean("true","id"));
|
||||||
|
cols.add(new SplitTableColBean("30%",SystemEnv.getHtmlLabelName(900,user.getLanguage()),"invoiceNumber","invoiceNumber",1).setIsInputCol(BoolAttr.TRUE));
|
||||||
|
cols.add(new SplitTableColBean("30%",SystemEnv.getHtmlLabelName(17213,user.getLanguage()),"invoiceCode","invoiceCode"));
|
||||||
|
cols.add(new SplitTableColBean("30%",SystemEnv.getHtmlLabelName(17213,user.getLanguage()),"invoiceTypeName","invoiceTypeName"));
|
||||||
|
cols.add(new SplitTableColBean("30%",SystemEnv.getHtmlLabelName(17213,user.getLanguage()),"billingdate","billingdate"));
|
||||||
|
cols.add(new SplitTableColBean("30%",SystemEnv.getHtmlLabelName(17213,user.getLanguage()),"taxIncludedPrice","taxIncludedPrice"));
|
||||||
|
SplitTableBean tableBean = new SplitTableBean(backFields,fromSql,sqlWhere.toString(),orderBy,"a.id","DESC",cols);
|
||||||
|
|
||||||
|
|
||||||
|
//移动端返回数据
|
||||||
|
List<SplitMobileDataBean> mobileDataBeanList=Util_MobileData.createList(JSON_CONFIG);
|
||||||
|
SplitMobileTemplateBean bean=Util_MobileData.createJsonTemplateBean("theme_default", mobileDataBeanList);
|
||||||
|
tableBean.createMobileTemplate(bean);
|
||||||
|
|
||||||
|
apidatas.putAll(SplitTableUtil.makeListDataResult(tableBean));
|
||||||
|
return apidatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高级搜索
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getBrowserConditionInfo(Map<String, Object> params) throws Exception {
|
||||||
|
Map<String, Object> apidatas = new HashMap<String, Object>();
|
||||||
|
List<SearchConditionItem> conditions = new ArrayList<SearchConditionItem>();
|
||||||
|
ConditionFactory conditionFactory = new ConditionFactory(user);
|
||||||
|
conditions.add(conditionFactory.createCondition(ConditionType.INPUT, 900, "invoiceNumber").setIsQuickSearch(true));
|
||||||
|
apidatas.put(BrowserConstant.BROWSER_RESULT_CONDITIONS, conditions);
|
||||||
|
return apidatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多发票(多选界面右侧选中部分)
|
||||||
|
* @param params
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getMultBrowserDestData(Map<String, Object> params) throws Exception {
|
||||||
|
Map<String, Object> apidatas = new HashMap<String, Object>();
|
||||||
|
|
||||||
|
String selectids = Util.null2String(params.get(BrowserConstant.BROWSER_MULT_DEST_SELECTIDS));
|
||||||
|
|
||||||
|
List<Map<String,Object>> datas = new ArrayList<Map<String,Object>>();
|
||||||
|
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
buffer.append(" select a.* from FnaInvoiceLedger a ");
|
||||||
|
buffer.append(" where a.id in (").append(selectids).append(")");
|
||||||
|
buffer.append(" order by a.id desc ");
|
||||||
|
|
||||||
|
RecordSet rs = new RecordSet();
|
||||||
|
rs.execute(buffer.toString());
|
||||||
|
while(rs.next()){
|
||||||
|
Map<String,Object> item = new HashMap<String,Object>();
|
||||||
|
item.put("id",Util.null2String(rs.getString("id")));
|
||||||
|
item.put("invoiceNumber",Util.null2String(rs.getString("invoiceNumber")));
|
||||||
|
item.put("invoiceCode",Util.null2String(rs.getString("invoiceCode")));
|
||||||
|
item.put("invoiceTypeName", Constants.INVOICETYPE.get(Util.null2String(rs.getString("invoicetype"))));
|
||||||
|
item.put("billingdate", Util.null2String(rs.getString("billingdate")));
|
||||||
|
item.put("taxIncludedPrice", Util.null2String(rs.getString("taxIncludedPrice")));
|
||||||
|
datas.add(item);
|
||||||
|
}
|
||||||
|
List<ListHeadBean> tableHeadColumns = new ArrayList<ListHeadBean>();
|
||||||
|
tableHeadColumns.add(new ListHeadBean("id",BoolAttr.TRUE).setIsPrimarykey(BoolAttr.TRUE));
|
||||||
|
tableHeadColumns.add(new ListHeadBean("invoiceNumber","",1,BoolAttr.TRUE));
|
||||||
|
tableHeadColumns.add(new ListHeadBean("invoiceCode",""));
|
||||||
|
tableHeadColumns.add(new ListHeadBean("invoiceTypeName",""));
|
||||||
|
tableHeadColumns.add(new ListHeadBean("billingdate",""));
|
||||||
|
tableHeadColumns.add(new ListHeadBean("taxIncludedPrice",""));
|
||||||
|
|
||||||
|
apidatas.put(BrowserConstant.BROWSER_RESULT_COLUMN, tableHeadColumns);
|
||||||
|
apidatas.put(BrowserConstant.BROWSER_RESULT_DATA, BrowserBaseUtil.sortDatas(datas,selectids,"id"));
|
||||||
|
apidatas.put(BrowserConstant.BROWSER_RESULT_TYPE, BrowserDataType.LIST_ALL_DATA.getTypeid());
|
||||||
|
|
||||||
|
return apidatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.controller;
|
||||||
|
|
||||||
|
import aiyh.utils.ApiResult;
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.service.ChangeFpytService;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.service.impl.ChangeFpytServiceImpl;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
@Path("/hcy_aphangxin")
|
||||||
|
public class ChangeFpytController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于处理修改发票用途的主要逻辑
|
||||||
|
*/
|
||||||
|
private ChangeFpytService changeFpytService = new ChangeFpytServiceImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志处理
|
||||||
|
*/
|
||||||
|
private Logger logger = Util.getLogger();
|
||||||
|
/**
|
||||||
|
* <h2>AP发票库-航信台账中修改发票用途</h2>
|
||||||
|
* @param request,response
|
||||||
|
* @return String
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/13 14:28
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/change/fpyt")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public String checkRequestInfo(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
String[] split = ids.split(",");
|
||||||
|
String s = changeFpytService.changeFpytValue(split);
|
||||||
|
return ApiResult.success(s);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.controller;
|
||||||
|
|
||||||
|
import aiyh.utils.ApiResult;
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.DeleteInvoiceNotApService;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.impl.DeleteInvoiceNotApServiceimpl;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Path("/hcy_aphangxin/delete")
|
||||||
|
public class DeleteInvoiceNotApController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志处理
|
||||||
|
*/
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service层、用于处理主要逻辑
|
||||||
|
*/
|
||||||
|
private final DeleteInvoiceNotApService deleteInvoiceNotApService = new DeleteInvoiceNotApServiceimpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>根据数据id删除非AP发票</h2>
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @return String
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/17 15:21
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/invoice/not/ap")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public String deleteInvoiceNotAp(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
//前端查询到的数据id
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
String[] split = ids.split(",");
|
||||||
|
//备份数据之前,首先查询要备份和要删除的数据的发票用途字段是否为非AP,若为非AP字段则删除,若不是非AP字段则不删除,并返回前端提醒用户选择ap字段进行删除
|
||||||
|
List<String> fpytList = deleteInvoiceNotApService.selectFpyt(split);
|
||||||
|
List<String> fpytCollection = fpytList.stream().filter(items -> items.equals("1")).collect(Collectors.toList());
|
||||||
|
if (fpytList.size() != fpytCollection.size()){
|
||||||
|
logger.info("删除数据发票用途字段不为非AP,请重新选择");
|
||||||
|
return ApiResult.success(1,"删除数据发票用途字段不为非AP,请重新选择");
|
||||||
|
}
|
||||||
|
//删除非AP数据之前首先备份数据
|
||||||
|
boolean backupBoolean = deleteInvoiceNotApService.backUpAfterDelete(split);
|
||||||
|
if (backupBoolean==true){
|
||||||
|
logger.info("数据备份成功");
|
||||||
|
}else{
|
||||||
|
logger.info("数据备份失败");
|
||||||
|
}
|
||||||
|
//开始删除数据
|
||||||
|
boolean deleteBoolean = deleteInvoiceNotApService.deleteDateById(split);
|
||||||
|
if (deleteBoolean){
|
||||||
|
logger.info("数据删除成功");
|
||||||
|
return ApiResult.success(0,"数据删除成功!");
|
||||||
|
}else {
|
||||||
|
logger.info("数据删除失败");
|
||||||
|
return ApiResult.success(1,"数据删除失败!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class BackupFnaDataEntity {
|
||||||
|
|
||||||
|
int requestId;
|
||||||
|
|
||||||
|
int formmodeid;
|
||||||
|
|
||||||
|
int modedatacreater;
|
||||||
|
|
||||||
|
int modedatacreatertype;
|
||||||
|
|
||||||
|
String modedatacreatedate;
|
||||||
|
|
||||||
|
String modedatacreatetime;
|
||||||
|
|
||||||
|
String MODEUUID;
|
||||||
|
|
||||||
|
String form_biz_id;
|
||||||
|
|
||||||
|
double taxRate;
|
||||||
|
|
||||||
|
String billingdate;
|
||||||
|
|
||||||
|
String invoicenumber;
|
||||||
|
|
||||||
|
String invoicecode;
|
||||||
|
|
||||||
|
String invoicetype;
|
||||||
|
|
||||||
|
double taxincludedprice;
|
||||||
|
|
||||||
|
double pricewithouttax;
|
||||||
|
|
||||||
|
double tax;
|
||||||
|
|
||||||
|
String purchaser;
|
||||||
|
|
||||||
|
String purchasertaxno;
|
||||||
|
|
||||||
|
String seller;
|
||||||
|
|
||||||
|
String salestaxno;
|
||||||
|
|
||||||
|
String purp;
|
||||||
|
|
||||||
|
String hxjksflr;
|
||||||
|
|
||||||
|
int checkStatus;
|
||||||
|
|
||||||
|
int status;
|
||||||
|
|
||||||
|
int authenticity;
|
||||||
|
|
||||||
|
int fpyt;
|
||||||
|
|
||||||
|
int check_status;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.mapper;
|
||||||
|
|
||||||
|
import aiyh.utils.annotation.recordset.*;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SqlMapper
|
||||||
|
public interface DeleteInvoiceNotApMapper {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Select("select * from fnainvoiceledger where id in ($t{split})")
|
||||||
|
List<Map<String, Object>> selectDate(@ParamMapper("split") String[] split);
|
||||||
|
|
||||||
|
@Insert("insert into uf_apfpscsj (billingDate,invoiceCode,invoiceNumber," +
|
||||||
|
"invoiceType,seller,purchaser,invoiceServiceYype,priceWithoutTax,taxRate," +
|
||||||
|
"tax,taxIncludedPrice,authenticity,reimbursementDate,reimbursePerson,requestId,userid_new," +
|
||||||
|
"invoiceSource_new,checkcode,status,card_id_new,encrypt_code_new,openid_new,wechatstatus,imageID,purchaserTaxNo," +
|
||||||
|
"salesTaxNo,entryTime,company_seal,form_type,form_name,kind,ciphertext,category,imageDocId,checkStatus," +
|
||||||
|
"updateOperate,cloudId,codeConfirm,numberConfirm,receiptor,reviewer,issuer,province,city,travel_tax,fylx," +
|
||||||
|
"purp,iele,orf,hxjksflr,fpyt) " +
|
||||||
|
"values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||||
|
boolean backupDate(@ParamMapper("backupDataLists") List<Object> backupDataLists);
|
||||||
|
|
||||||
|
@Delete("delete from fnainvoiceledger where id = #{id}")
|
||||||
|
boolean deleteDateById(@ParamMapper("id") String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>查询发票用途字段是否为非航行</h2>
|
||||||
|
* @param id 数据id
|
||||||
|
* @return String
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/20 13:56
|
||||||
|
*/
|
||||||
|
@Select("select fpyt from fnainvoiceledger where id = #{id}")
|
||||||
|
String selectFpyt(String id);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface DeleteInvoiceNotApService {
|
||||||
|
boolean backUpAfterDelete(String[] split);
|
||||||
|
boolean deleteDateById(String[] split);
|
||||||
|
|
||||||
|
List<String> selectFpyt(String[] split);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.mapper.DeleteInvoiceNotApMapper;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.DeleteInvoiceNotApService;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DeleteInvoiceNotApServiceimpl implements DeleteInvoiceNotApService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于sql处理
|
||||||
|
*/
|
||||||
|
private final DeleteInvoiceNotApMapper deleteInvoiceNotApMapper = Util.getMapper(DeleteInvoiceNotApMapper.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志
|
||||||
|
*/
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>用于查询数据,查询到的数据进行数据备份</h2>
|
||||||
|
* @param split requestid
|
||||||
|
* @return boolean 数据备份是否成功
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/17 17:48
|
||||||
|
*/
|
||||||
|
public boolean backUpAfterDelete(String[] split) {
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
for (String id : split) {
|
||||||
|
String backupDataSql = "INSERT INTO fnabackup (billingdate,\t\n" +
|
||||||
|
"invoicenumber,\t\n" +
|
||||||
|
"invoicecode,\n" +
|
||||||
|
"invoicetype,\t\n" +
|
||||||
|
"taxincludedprice,pricewithouttax,\t\n" +
|
||||||
|
"tax,\n" +
|
||||||
|
"purchaser,purchasertaxno,\t\n" +
|
||||||
|
"seller,salestaxno,purp,hxjksflr,checkStatus,status,authenticity,fpyt,taxrate\n" +
|
||||||
|
") SELECT \n" +
|
||||||
|
"billingdate,\t\n" +
|
||||||
|
"invoicenumber,\t\n" +
|
||||||
|
"invoicecode,\n" +
|
||||||
|
"invoicetype,\t\n" +
|
||||||
|
"taxincludedprice,pricewithouttax,\t\n" +
|
||||||
|
"tax,\n" +
|
||||||
|
"purchaser,purchasertaxno,\t\n" +
|
||||||
|
"seller,salestaxno,purp,hxjksflr,checkStatus,status,authenticity,fpyt,taxrate\n" +
|
||||||
|
" FROM fnainvoiceledger WHERE id = ?";
|
||||||
|
boolean backupBool = recordSet.executeUpdate(backupDataSql, id);
|
||||||
|
if (!backupBool){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//开始备份数据
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>根据数据id、删除数据</h2>
|
||||||
|
* @param split id
|
||||||
|
* @return boolean 数据删除是否成功
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/17 18:00
|
||||||
|
*/
|
||||||
|
public boolean deleteDateById(String[] split) {
|
||||||
|
List<Boolean> booleanList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String s : split) {
|
||||||
|
boolean deleteBoolean = deleteInvoiceNotApMapper.deleteDateById(s);
|
||||||
|
booleanList.add(deleteBoolean);
|
||||||
|
}
|
||||||
|
List<Boolean> collect = booleanList.stream().distinct().collect(Collectors.toList());
|
||||||
|
if (collect != null && collect.size()==1){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectFpyt(String[] split) {
|
||||||
|
List<String> fpytLists = new ArrayList<>();
|
||||||
|
for (String s : split) {
|
||||||
|
String fpyt = deleteInvoiceNotApMapper.selectFpyt(s);
|
||||||
|
fpytLists.add(fpyt);
|
||||||
|
}
|
||||||
|
return fpytLists;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.mapper.DeleteInvoiceNotApMapper;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.deleteinvoicenotap.service.DeleteInvoiceNotApService;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DeleteInvoiceNotApServiceimpl_copy implements DeleteInvoiceNotApService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于sql处理
|
||||||
|
*/
|
||||||
|
private final DeleteInvoiceNotApMapper deleteInvoiceNotApMapper = Util.getMapper(DeleteInvoiceNotApMapper.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志
|
||||||
|
*/
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>用于查询数据,查询到的数据进行数据备份</h2>
|
||||||
|
* @param split requestid
|
||||||
|
* @return boolean 数据备份是否成功
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/17 17:48
|
||||||
|
*/
|
||||||
|
public boolean backUpAfterDelete(String[] split) {
|
||||||
|
//导入数据之前先查询数据
|
||||||
|
List<Map<String,Object>> backupDate = deleteInvoiceNotApMapper.selectDate(split);//拿到需要备份的数据
|
||||||
|
//开始备份数据
|
||||||
|
List<Boolean> bool = new ArrayList<>();//存放sql语句insert动作是否成功
|
||||||
|
for (Map<String, Object> map : backupDate) {
|
||||||
|
List<Object> backupDataLists = new ArrayList<>();
|
||||||
|
backupDataLists.add(map.get("billingDate"));
|
||||||
|
backupDataLists.add(map.get("invoiceCode"));
|
||||||
|
backupDataLists.add(map.get("invoiceNumber"));
|
||||||
|
backupDataLists.add(map.get("invoiceType"));
|
||||||
|
backupDataLists.add(map.get("seller"));
|
||||||
|
backupDataLists.add(map.get("purchaser"));
|
||||||
|
backupDataLists.add(map.get("invoiceServiceYype"));
|
||||||
|
backupDataLists.add(map.get("priceWithoutTax"));
|
||||||
|
backupDataLists.add(map.get("taxRate"));
|
||||||
|
backupDataLists.add(map.get("tax"));
|
||||||
|
backupDataLists.add(map.get("taxIncludedPrice"));
|
||||||
|
backupDataLists.add(map.get("authenticity"));
|
||||||
|
backupDataLists.add(map.get("reimbursementDate"));
|
||||||
|
backupDataLists.add(map.get("reimbursePerson"));
|
||||||
|
backupDataLists.add(map.get("requestId"));
|
||||||
|
backupDataLists.add(map.get("useridNew"));
|
||||||
|
backupDataLists.add(map.get("invoiceSourceNew"));
|
||||||
|
backupDataLists.add(map.get("checkcode"));
|
||||||
|
backupDataLists.add(map.get("status"));
|
||||||
|
backupDataLists.add(map.get("cardIdNew"));
|
||||||
|
backupDataLists.add(map.get("encryptCodeNew"));
|
||||||
|
backupDataLists.add(map.get("openidNew"));
|
||||||
|
backupDataLists.add(map.get("wechatstatus"));
|
||||||
|
backupDataLists.add(map.get("imageID"));
|
||||||
|
backupDataLists.add(map.get("purchaserTaxNo"));
|
||||||
|
backupDataLists.add(map.get("salesTaxNo"));
|
||||||
|
backupDataLists.add(map.get("entryTime"));
|
||||||
|
backupDataLists.add(map.get("companySeal"));
|
||||||
|
backupDataLists.add(map.get("formType"));
|
||||||
|
backupDataLists.add(map.get("formName"));
|
||||||
|
backupDataLists.add(map.get("kind"));
|
||||||
|
backupDataLists.add(map.get("ciphertext"));
|
||||||
|
backupDataLists.add(map.get("category"));
|
||||||
|
backupDataLists.add(map.get("imageDocId"));
|
||||||
|
backupDataLists.add(map.get("checkStatus"));
|
||||||
|
backupDataLists.add(map.get("updateOperate"));
|
||||||
|
backupDataLists.add(map.get("cloudId"));
|
||||||
|
backupDataLists.add(map.get("codeConfirm"));
|
||||||
|
backupDataLists.add(map.get("numberConfirm"));
|
||||||
|
backupDataLists.add(map.get("receiptor"));
|
||||||
|
backupDataLists.add(map.get("reviewer"));
|
||||||
|
backupDataLists.add(map.get("issuer"));
|
||||||
|
backupDataLists.add(map.get("province"));
|
||||||
|
backupDataLists.add(map.get("city"));
|
||||||
|
backupDataLists.add(map.get("travelTax"));
|
||||||
|
// backupDataLists.add(map.get("fylx"));
|
||||||
|
backupDataLists.add(map.get("purp"));
|
||||||
|
// backupDataLists.add(map.get("iele"));
|
||||||
|
backupDataLists.add(map.get("orf"));
|
||||||
|
backupDataLists.add(map.get("hxjksflr"));
|
||||||
|
backupDataLists.add(map.get("fpyt"));
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String backupdate = "insert into fnabackup (billingDate,invoiceCode,invoiceNumber," +
|
||||||
|
"invoiceType,seller,purchaser,invoiceServiceYype,priceWithoutTax,taxRate," +
|
||||||
|
"tax,taxIncludedPrice,authenticity,reimbursementDate,reimbursePerson,requestId,userid_new," +
|
||||||
|
"invoiceSource_new,checkcode,status,card_id_new,encrypt_code_new,openid_new,wechatstatus,imageID,purchaserTaxNo," +
|
||||||
|
"salesTaxNo,entryTime,company_seal,form_type,form_name,kind,ciphertext,category,imageDocId,checkStatus," +
|
||||||
|
"updateOperate,cloudId,codeConfirm,numberConfirm,receiptor,reviewer,issuer,province,city,travel_tax," +
|
||||||
|
"purp,orf,hxjksflr,fpyt) " +
|
||||||
|
"values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||||
|
boolean insertIntoBoolean = recordSet.executeUpdate(backupdate, backupDataLists);
|
||||||
|
// boolean insertIntoBoolean = deleteInvoiceNotApMapper.backupDate(backupDataLists);
|
||||||
|
bool.add(insertIntoBoolean);
|
||||||
|
logger.info("插入语句是否成功==="+insertIntoBoolean);
|
||||||
|
}
|
||||||
|
List<Boolean> collect = bool.stream().distinct().collect(Collectors.toList());
|
||||||
|
if (collect!=null && collect.size()==1){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>根据数据id、删除数据</h2>
|
||||||
|
* @param split id
|
||||||
|
* @return boolean 数据删除是否成功
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/17 18:00
|
||||||
|
*/
|
||||||
|
public boolean deleteDateById(String[] split) {
|
||||||
|
List<Boolean> booleanList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String s : split) {
|
||||||
|
boolean deleteBoolean = deleteInvoiceNotApMapper.deleteDateById(s);
|
||||||
|
booleanList.add(deleteBoolean);
|
||||||
|
}
|
||||||
|
List<Boolean> collect = booleanList.stream().distinct().collect(Collectors.toList());
|
||||||
|
if (collect != null && collect.size()==1){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> selectFpyt(String[] split) {
|
||||||
|
List<String> fpytLists = new ArrayList<>();
|
||||||
|
for (String s : split) {
|
||||||
|
String fpyt = deleteInvoiceNotApMapper.selectFpyt(s);
|
||||||
|
fpytLists.add(fpyt);
|
||||||
|
}
|
||||||
|
return fpytLists;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.mapper;
|
||||||
|
|
||||||
|
import aiyh.utils.annotation.recordset.ParamMapper;
|
||||||
|
import aiyh.utils.annotation.recordset.SqlMapper;
|
||||||
|
import aiyh.utils.annotation.recordset.Update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>根据数据id,修改发票用途字段值</h1>
|
||||||
|
* @Author hcy
|
||||||
|
* @Date 2023/3/13 14:41
|
||||||
|
*/
|
||||||
|
@SqlMapper
|
||||||
|
public interface ChangeFpytMapper {
|
||||||
|
|
||||||
|
@Update("update fnainvoiceledger set fpyt = 1 where id = #{id}")
|
||||||
|
boolean updateFpytValue(@ParamMapper("id") String id);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.service;
|
||||||
|
|
||||||
|
public interface ChangeFpytService {
|
||||||
|
/**
|
||||||
|
* <h2>修改发票用途主要逻辑</h2>
|
||||||
|
* @param split
|
||||||
|
* @return String
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/3/13 14:38
|
||||||
|
*/
|
||||||
|
public String changeFpytValue(String[] split);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.api.chaoyang.he.hcy_aphangxin.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.mapper.ChangeFpytMapper;
|
||||||
|
import com.api.chaoyang.he.hcy_aphangxin.service.ChangeFpytService;
|
||||||
|
|
||||||
|
|
||||||
|
public class ChangeFpytServiceImpl implements ChangeFpytService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于直接操作数据库的Mapper类
|
||||||
|
*/
|
||||||
|
private final ChangeFpytMapper changeFpytMapper = Util.getMapper(ChangeFpytMapper.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String changeFpytValue(String[] split) {
|
||||||
|
int num = 0;
|
||||||
|
for (String s : split) {
|
||||||
|
if (!s.equals("")){
|
||||||
|
boolean b = changeFpytMapper.updateFpytValue(s);
|
||||||
|
if (b){
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num == split.length){
|
||||||
|
return "所有数据更新成功";
|
||||||
|
}else {
|
||||||
|
return "数据更新失败";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.service.ExportPDFService;
|
||||||
|
import com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.service.impl.ExportPDFServiceImpl;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import weaver.hrm.HrmUserVarify;
|
||||||
|
import weaver.hrm.User;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Path("/hcy_dingxinjituan")
|
||||||
|
public class ExportPDFController {
|
||||||
|
|
||||||
|
private final ExportPDFService exportPDFService = new ExportPDFServiceImpl();
|
||||||
|
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/export/pdf")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response exportPDF(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
|
||||||
|
Response build = null;
|
||||||
|
Response.ResponseBuilder header = null;
|
||||||
|
StreamingOutput output = null;
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
String[] split = ids.split(",");
|
||||||
|
//第二步更新导出次+1
|
||||||
|
exportPDFService.updateDccs(split);
|
||||||
|
|
||||||
|
//第一步查询数据封装到List<Map<String,Object>>类型的集合中
|
||||||
|
List<Map<String,Object>> dataList = exportPDFService.selectDataList(split);
|
||||||
|
User user = HrmUserVarify.getUser(request, response);
|
||||||
|
String exportUser = user.getLastname();
|
||||||
|
//第三步同步数据
|
||||||
|
List<Boolean> insertBooleanLists = exportPDFService.backupData(dataList,exportUser,this.getCurrentTime());
|
||||||
|
for (Boolean list : insertBooleanLists) {
|
||||||
|
if (list == false){
|
||||||
|
logger.info("备份数据失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//第四步根据第一步拿到的数据导出pdf
|
||||||
|
String exportTime = getCurrentDateTime();//导出时间
|
||||||
|
|
||||||
|
|
||||||
|
byte[] pdfBytes = exportPDFService.exportPDF(dataList,exportUser,exportTime);
|
||||||
|
output = outputStream -> {
|
||||||
|
outputStream.write(pdfBytes);
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
String fileName = "顶新集团-"+ getCurrentTime() + ".pdf";
|
||||||
|
try {
|
||||||
|
header = Response.ok(output,MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.type("application/xlsx")
|
||||||
|
.header("Content-Disposition",
|
||||||
|
"attachment; filename=\""+ new String(fileName.getBytes("GBK"), StandardCharsets.ISO_8859_1)+"\"");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return header.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentDateTime() {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
return now.format(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCurrentTime() {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
return now.format(formatter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.mapper;
|
||||||
|
|
||||||
|
import aiyh.utils.annotation.recordset.ParamMapper;
|
||||||
|
import aiyh.utils.annotation.recordset.Select;
|
||||||
|
import aiyh.utils.annotation.recordset.SqlMapper;
|
||||||
|
import aiyh.utils.annotation.recordset.Update;
|
||||||
|
import org.directwebremoting.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SqlMapper
|
||||||
|
public interface ExportPDFMapper {
|
||||||
|
|
||||||
|
|
||||||
|
@Select("select * from uf_sapywdj where id in ($t{split})")
|
||||||
|
List<Map<String, Object>> selectDataList(String[] split);
|
||||||
|
|
||||||
|
@Update("update uf_sapywdj set dccs = #{s} where id = #{id}")
|
||||||
|
boolean updateDccs(@ParamMapper("s") String s, @ParamMapper("id") String id);
|
||||||
|
|
||||||
|
|
||||||
|
@Select("select dccs from uf_sapywdj where id = #{id}")
|
||||||
|
String selectDccs(String id);
|
||||||
|
|
||||||
|
@Select("select requestname from workflow_requestbase where requestid = #{stringXglc}")
|
||||||
|
String selectRequestName(String stringXglc);
|
||||||
|
|
||||||
|
@Select("select gysmc from uf_sapgys where id= #{gfdw} ")
|
||||||
|
String setGfdw(String gfdw);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface ExportPDFService {
|
||||||
|
List<Map<String, Object>> selectDataList(String[] split);
|
||||||
|
|
||||||
|
void updateDccs(String[] split);
|
||||||
|
|
||||||
|
byte[] exportPDF(List<Map<String, Object>> dataList, String exportUser, String exportTime);
|
||||||
|
|
||||||
|
List<Boolean> backupData(List<Map<String, Object>> dataList,String exportUser,String exportTime);
|
||||||
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
package com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.mapper.ExportPDFMapper;
|
||||||
|
import com.api.chaoyang.he.hcy_dingxinjituan.exportpdf.service.ExportPDFService;
|
||||||
|
import com.itextpdf.text.Document;
|
||||||
|
import com.itextpdf.text.*;
|
||||||
|
import com.itextpdf.text.pdf.*;
|
||||||
|
import de.schlichtherle.io.File;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
import weaver.general.GCONST;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExportPDFServiceImpl implements ExportPDFService {
|
||||||
|
|
||||||
|
private final ExportPDFMapper exportPDFMapper = Util.getMapper(ExportPDFMapper.class);
|
||||||
|
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> selectDataList(String[] split) {
|
||||||
|
return exportPDFMapper.selectDataList(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDccs(String[] split) {
|
||||||
|
for (String s : split) {
|
||||||
|
String dccs = Util.null2String(exportPDFMapper.selectDccs(s));
|
||||||
|
if (!"".equals(dccs) && Util.getIntValue(dccs)!=-1){
|
||||||
|
int dccsInt = Integer.parseInt(dccs);
|
||||||
|
dccsInt++;
|
||||||
|
dccs = String.valueOf(dccsInt);
|
||||||
|
boolean updateBool = exportPDFMapper.updateDccs(dccs,s);
|
||||||
|
}else if (!"".equals(dccs) && Util.getIntValue(dccs)==-1){
|
||||||
|
boolean updateBool = exportPDFMapper.updateDccs("1",s);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
boolean updateBool = exportPDFMapper.updateDccs("1",s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] exportPDF(List<Map<String, Object>> dataList, String exportUser, String exportTime) {
|
||||||
|
|
||||||
|
// 创建PDF文档
|
||||||
|
Document document = new Document();
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
PdfWriter.getInstance(document, baos);
|
||||||
|
// 打开PDF文档
|
||||||
|
document.open();
|
||||||
|
|
||||||
|
// 创建页面并设置属性
|
||||||
|
// Rectangle pageSize = PageSize.A4;
|
||||||
|
// document.setPageSize(pageSize.rotate()); // 横向页面
|
||||||
|
// document.setMargins(36, 36, 36, 36); // 36pt边距
|
||||||
|
|
||||||
|
// 创建表格
|
||||||
|
PdfPTable table = new PdfPTable(10);
|
||||||
|
table.setWidthPercentage(100f);
|
||||||
|
table.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
|
table.setHeaderRows(1);
|
||||||
|
//字体
|
||||||
|
String rootPath = GCONST.getRootPath();
|
||||||
|
// new File(GCONST.getRootPath()+"WEB-INF"+File.separatorChar+"meeting"+File.separatorChar);
|
||||||
|
// String fontPath = rootPath + "/WEB-INF/font/simfang.ttf";
|
||||||
|
String fontPath = "/app/ecology/WEB-INF/font/simfang.ttf";
|
||||||
|
logger.info("fontPath==="+fontPath);
|
||||||
|
// BaseFont bfChinese = BaseFont.createFont("D:\\WEAVER\\ecology\\WEB-INF\\font\\simfang.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
|
||||||
|
BaseFont bfChinese = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
|
||||||
|
|
||||||
|
|
||||||
|
// 添加标题
|
||||||
|
Font titleFont = new Font(bfChinese, 18, Font.BOLD);
|
||||||
|
Paragraph title = new Paragraph("SAP-业务单据数据", titleFont);
|
||||||
|
title.setAlignment(Element.ALIGN_CENTER);
|
||||||
|
document.add(title);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//// 添加导出人和导出日期信息
|
||||||
|
|
||||||
|
Paragraph info = new Paragraph("导出人:"+exportUser+" 导出日期:"+exportTime,
|
||||||
|
new Font(bfChinese, 10f, Font.NORMAL, BaseColor.BLACK));
|
||||||
|
info.setAlignment(Element.ALIGN_RIGHT);
|
||||||
|
document.add(info);
|
||||||
|
|
||||||
|
// 添加表头行
|
||||||
|
// 设置表头样式及内容
|
||||||
|
Font headerFont = new Font(bfChinese, 10, Font.BOLD, BaseColor.WHITE);
|
||||||
|
PdfPCell headerCell = new PdfPCell();
|
||||||
|
headerCell.setBackgroundColor(BaseColor.BLACK);
|
||||||
|
headerCell.setHorizontalAlignment(Element.ALIGN_CENTER);
|
||||||
|
headerCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
headerCell.setPadding(5);
|
||||||
|
|
||||||
|
headerCell.setPhrase(new Phrase("申请编号", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("相关流程", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("法人公司编码", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("地址号", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("给付单位", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("开户行", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("银行账号", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("金额", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("SAP凭证号", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
headerCell.setPhrase(new Phrase("导出次数", headerFont));
|
||||||
|
table.addCell(headerCell);
|
||||||
|
|
||||||
|
// 添加数据行
|
||||||
|
|
||||||
|
Font dataFont = new Font(bfChinese, 9, Font.NORMAL, BaseColor.BLACK);
|
||||||
|
for (Map<String, Object> row : dataList) {
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("sqbh")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
String stringXglc = Util.null2String(row.get("xglc"));
|
||||||
|
String requstName = exportPDFMapper.selectRequestName(stringXglc);
|
||||||
|
table.addCell(createCell(requstName, dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("frgsbm")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("dzh")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
String gfdw = Util.null2String(row.get("gfdw"));
|
||||||
|
String gfdwString = exportPDFMapper.setGfdw(gfdw);
|
||||||
|
table.addCell(createCell(gfdwString, dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("khh")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("yhzh")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("je")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("sappzh")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
table.addCell(createCell(String.valueOf(row.get("dccs")), dataFont, Element.ALIGN_CENTER));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置表格样式和行样式
|
||||||
|
table.setSpacingBefore(8f);
|
||||||
|
table.setSpacingAfter(8f);
|
||||||
|
table.getDefaultCell().setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
table.getDefaultCell().setPadding(4f);
|
||||||
|
|
||||||
|
// 添加表格到页面
|
||||||
|
document.add(table);
|
||||||
|
|
||||||
|
// 添加导出人和导出日期信息
|
||||||
|
|
||||||
|
|
||||||
|
// 关闭PDF文档
|
||||||
|
document.close();
|
||||||
|
|
||||||
|
// 将PDF文档写入输出流
|
||||||
|
return baos.toByteArray();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.info("导出PDF文档异常原因==="+e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Boolean> backupData(List<Map<String, Object>> dataList,String exportuser,String exporttime) {
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
List<Boolean> insertBooleanLists = new ArrayList();
|
||||||
|
try {
|
||||||
|
for (Map<String, Object> map : dataList) {
|
||||||
|
//toDo:
|
||||||
|
String insertSql = "insert into uf_sapywdj_backup (requestId,formmodeid,modedatacreater,modedatacreatertype," +
|
||||||
|
"modedatacreatedate,modedatacreatetime,MODEUUID,lclx,sfmdfy,qklx,fkfs,yfklx,dx,xglc,sqr,cgbs,fhxx,sappzh,sqbm," +
|
||||||
|
"khh,yhzh,ytsm,je,frgsbm,dpmc,dpbh,qwfkrq,dzh,szgs,sqbh,cwgzrq,ph,cwjb,paystatus,completetime,fygz,jybgse,jybgwsje,jybghsje," +
|
||||||
|
"jybgfhxx1,jybgfhxx2,jybgfhxx3,jybgfhzt1,jybgfhzt2,jybgfhzt3,jybgpzh1,jybgpzh2,jybgpzh3,dh,jybglshjl,gtddh,frgs,gfdw,cbzx,ywlx,mdcwjb," +
|
||||||
|
"mdcwkzg,cwkzg,zbchbzg,zbchbzg1,mdcwkzg1,dpmc1,zhcyr,modedatamodifier,modedatamodifydatetime,dccs,exportuser,exporttime) values (?,?,?,?,?,?,?,?,?,?," +
|
||||||
|
"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
|
||||||
|
logger.info("sql==="+insertSql);
|
||||||
|
boolean insertBool = recordSet.executeUpdate(insertSql,
|
||||||
|
Util.null2String(map.get("requestId")),
|
||||||
|
Util.null2String(map.get("formmodeid")),
|
||||||
|
Util.null2String(map.get("formmodeid")),
|
||||||
|
Util.null2String(map.get("modedatacreatertype")),
|
||||||
|
Util.null2String(map.get("modedatacreatedate")),
|
||||||
|
Util.null2String(map.get("modedatacreatetime")),
|
||||||
|
Util.null2String(map.get("MODEUUID")),
|
||||||
|
Util.null2String(map.get("lclx")),
|
||||||
|
Util.null2String(map.get("sfmdfy")),
|
||||||
|
Util.null2String(map.get("qklx")),
|
||||||
|
Util.null2String(map.get("fkfs")),
|
||||||
|
Util.null2String(map.get("yfklx")),
|
||||||
|
Util.null2String(map.get("dx")),
|
||||||
|
Util.null2String(map.get("xglc")),
|
||||||
|
Util.null2String(map.get("sqr")),
|
||||||
|
Util.null2String(map.get("cgbs")),
|
||||||
|
Util.null2String(map.get("fhxx")),
|
||||||
|
Util.null2String(map.get("sappzh")),
|
||||||
|
Util.null2String(map.get("sqbm")),
|
||||||
|
Util.null2String(map.get("khh")),
|
||||||
|
Util.null2String(map.get("yhzh")),
|
||||||
|
Util.null2String(map.get("ytsm")),
|
||||||
|
Util.null2String(map.get("je")),
|
||||||
|
Util.null2String(map.get("frgsbm")),
|
||||||
|
Util.null2String(map.get("dpmc")),
|
||||||
|
Util.null2String(map.get("dpbh")),
|
||||||
|
Util.null2String(map.get("qwfkrq")),
|
||||||
|
Util.null2String(map.get("dzh")),
|
||||||
|
Util.null2String(map.get("szgs")),
|
||||||
|
Util.null2String(map.get("sqbh")),
|
||||||
|
Util.null2String(map.get("cwgzrq")),
|
||||||
|
Util.null2String(map.get("ph")),
|
||||||
|
Util.null2String(map.get("cwjb")),
|
||||||
|
Util.null2String(map.get("paystatus")),
|
||||||
|
Util.null2String(map.get("completetime")),
|
||||||
|
Util.null2String(map.get("fygz")),
|
||||||
|
Util.null2String(map.get("jybgse")),
|
||||||
|
Util.null2String(map.get("jybgwsje")),
|
||||||
|
Util.null2String(map.get("jybghsje")),
|
||||||
|
Util.null2String(map.get("jybgfhxx1")),
|
||||||
|
Util.null2String(map.get("jybgfhxx2")),
|
||||||
|
Util.null2String(map.get("jybgfhxx3")),
|
||||||
|
Util.null2String(map.get("jybgfhzt1")),
|
||||||
|
Util.null2String(map.get("jybgfhzt2")),
|
||||||
|
Util.null2String(map.get("jybgfhzt3")),
|
||||||
|
Util.null2String(map.get("jybgpzh1")),
|
||||||
|
Util.null2String(map.get("jybgpzh2")),
|
||||||
|
Util.null2String(map.get("jybgpzh3")),
|
||||||
|
Util.null2String(map.get("dh")),
|
||||||
|
Util.null2String(map.get("jybglshjl")),
|
||||||
|
Util.null2String(map.get("gtddh")),
|
||||||
|
Util.null2String(map.get("frgs")),
|
||||||
|
Util.null2String(map.get("gfdw")),
|
||||||
|
Util.null2String(map.get("cbzx")),
|
||||||
|
Util.null2String(map.get("ywlx")),
|
||||||
|
Util.null2String(map.get("mdcwjb")),
|
||||||
|
Util.null2String(map.get("mdcwkzg")),
|
||||||
|
Util.null2String(map.get("cwkzg")),
|
||||||
|
Util.null2String(map.get("zbchbzg")),
|
||||||
|
Util.null2String(map.get("zbchbzg1")),
|
||||||
|
Util.null2String(map.get("mdcwkzg1")),
|
||||||
|
Util.null2String(map.get("dpmc1")),
|
||||||
|
Util.null2String(map.get("zhcyr")),
|
||||||
|
Util.null2String(map.get("modedatamodifier")),
|
||||||
|
Util.null2String(map.get("modedatamodifydatetime")),
|
||||||
|
Util.null2String(map.get("dccs")),
|
||||||
|
exportuser,
|
||||||
|
exporttime
|
||||||
|
);
|
||||||
|
insertBooleanLists.add(insertBool);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.error("错误原因==="+e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return insertBooleanLists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PdfPCell createCell(String text, Font font, int align) {
|
||||||
|
PdfPCell cell = new PdfPCell(new Phrase(text, font));
|
||||||
|
cell.setHorizontalAlignment(align);
|
||||||
|
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
|
||||||
|
cell.setPadding(4f);
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.controller;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_fengtianfangzhi.service.DetailDataExportExcelApi;
|
||||||
|
import com.api.chaoyang.he.hcy_fengtianfangzhi.service.impl.DetailDataExportExcelService;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
@Path("/hcy_fengtianfangzhi")
|
||||||
|
public class DetailDataExportExcelController {
|
||||||
|
|
||||||
|
|
||||||
|
private DetailDataExportExcelApi detailDataExportExcelService = new DetailDataExportExcelService();
|
||||||
|
|
||||||
|
|
||||||
|
private Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*导出AVPN Excel表格
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/detailDataExportExcel")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response checkRequestInfo(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
|
||||||
|
String requestid = request.getParameter("requestid");
|
||||||
|
logger.info("requestid=="+requestid);
|
||||||
|
String lsxjd = request.getParameter("lsxjd");
|
||||||
|
logger.info("lsxjd=="+lsxjd);//lsxjd
|
||||||
|
Response response1 = detailDataExportExcelService.exportExcel(requestid,lsxjd);
|
||||||
|
return response1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,280 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.controller;
|
||||||
|
|
||||||
|
import aiyh.utils.ApiResult;
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.engine.workflow.biz.requestForm.WfToDocBiz;
|
||||||
|
import com.engine.workflow.biz.requestForm.WfWaterMark4WfToDocBiz;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
import weaver.file.FileUpload;
|
||||||
|
import weaver.file.ImageFileManager;
|
||||||
|
import weaver.hrm.HrmUserVarify;
|
||||||
|
import weaver.hrm.User;
|
||||||
|
import weaver.interfaces.workflow.action.WorkflowToDoc;
|
||||||
|
import weaver.system.SystemComInfo;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h1>导出状态修改</h1>
|
||||||
|
* @Author jiacheng.deng
|
||||||
|
* @Date 2023/1/18 14:50
|
||||||
|
*/
|
||||||
|
//"tb/djc/changeStatus/download/requestid=" + requestId + "&workflowid=" + workflowId + "&nodeid=" + nodeId
|
||||||
|
@Path("/tb")
|
||||||
|
public class TbSheetChangeDownloadStatusController {
|
||||||
|
|
||||||
|
|
||||||
|
//流程保存文档源码
|
||||||
|
private WorkflowToDoc workflowToDoc = new WorkflowToDoc();
|
||||||
|
//流程保存文档源码
|
||||||
|
private WfToDocBiz wfToDocBiz = new WfToDocBiz();
|
||||||
|
//数据库链接驱动
|
||||||
|
private RecordSet recordSet = new RecordSet();
|
||||||
|
//日志
|
||||||
|
private final Logger log = Util.getLogger();
|
||||||
|
//底部签字意见列表显示数量
|
||||||
|
private int pageSize =100;
|
||||||
|
//下载的输入流
|
||||||
|
private ImageFileManager imageFileManager = new ImageFileManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>修改导出状态</h2>
|
||||||
|
* @param request,response
|
||||||
|
* @return String
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/changeStatus/download")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response changeStatus(@Context HttpServletRequest request,
|
||||||
|
@Context HttpServletResponse response
|
||||||
|
) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
User logInUser = HrmUserVarify.getUser(request, response);
|
||||||
|
String uid = String.valueOf(logInUser.getUID());
|
||||||
|
log.info("用户的id===>" + uid);
|
||||||
|
String findUserRole = "select * from HrmRoleMembers where resourceid = " + uid;
|
||||||
|
boolean b = recordSet.execute(findUserRole);
|
||||||
|
log.info("查询用户数据sql===> " + findUserRole + " ===>是否执行 :" + b);
|
||||||
|
recordSet.next();
|
||||||
|
String roleid = recordSet.getString("roleid");
|
||||||
|
log.info("用户角色roleid===>" + roleid);
|
||||||
|
|
||||||
|
// log.info("requsetid===>" + requestid + "workflowid===>" + workflowid + "nodeid===>" + nodeid);
|
||||||
|
|
||||||
|
|
||||||
|
if ("1031".equals(roleid)) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// StreamingOutput downloadStream = download2changeStatus(uid,request,response,requestid,workflowid,nodeid);
|
||||||
|
|
||||||
|
// InputStream is = imageFileManager.getInputStreamById(this.getImageFileId(requestid));
|
||||||
|
|
||||||
|
String updateDownloadStatusSQl = "update formtable_main_368 set dczt = ?" ;
|
||||||
|
|
||||||
|
int flag = 1;
|
||||||
|
boolean b1 = recordSet.executeUpdate(updateDownloadStatusSQl, flag);
|
||||||
|
log.info("执行修改导出状态代码===>" + b1);
|
||||||
|
|
||||||
|
|
||||||
|
// return Response.ok(downloadStream, MediaType.APPLICATION_OCTET_STREAM).type("application/")
|
||||||
|
// .header("Content-Disposition", "attachment;filename=workflow.pdf").build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("转换失败:" + e.toString());
|
||||||
|
return Response.ok(ApiResult.error("出现错误,错误原因: " + e), MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// StreamingOutput downloadStream = download2changeStatus(uid,request,response,requestid,workflowid,nodeid);
|
||||||
|
// return Response.ok(downloadStream, MediaType.APPLICATION_OCTET_STREAM).type("application/pdf")
|
||||||
|
// .header("Content-Disposition", "attachment;filename=contracts.zip").build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("转换失败:" + e.toString());
|
||||||
|
|
||||||
|
return Response.ok(ApiResult.error("出现错误,错误原因: " + e), MediaType.APPLICATION_JSON).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(Exception e){
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>修改导出状态</h2>
|
||||||
|
* @param
|
||||||
|
* @return
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
public StreamingOutput download2changeStatus(String uid,HttpServletRequest request,HttpServletResponse response,String requestid,String workflowid,String nodeid){
|
||||||
|
|
||||||
|
LinkedHashMap<String, String> fileids = new LinkedHashMap<String, String> ();
|
||||||
|
|
||||||
|
User user = new User(weaver.general.Util.getIntValue(uid));
|
||||||
|
|
||||||
|
String docFiles = getDocFiles(workflowid);
|
||||||
|
//是否签名
|
||||||
|
int keepSign = getKeepSign(workflowid);
|
||||||
|
String filename = UUID.randomUUID().toString();
|
||||||
|
String temppath = getFileSavePath();
|
||||||
|
|
||||||
|
//是否开启水印
|
||||||
|
boolean isOpenWaterMark = WfWaterMark4WfToDocBiz.isOpenWaterMark(weaver.general.Util.getIntValue(workflowid));
|
||||||
|
//在这里先获取到modeid 线程中获取时流程可能已经到了下个节点导致模板获取的不对
|
||||||
|
String modeid = wfToDocBiz.getModeid(Util.getIntValue(requestid), Util.getIntValue(workflowid), Util.getIntValue(nodeid));
|
||||||
|
|
||||||
|
WfToDocBiz wfToDocBiz = new WfToDocBiz(user,pageSize,keepSign,docFiles,modeid,isOpenWaterMark);
|
||||||
|
wfToDocBiz.generatepdfandhtml(requestid,filename,temppath,"1");
|
||||||
|
fileids.putAll(wfToDocBiz.getfileids("",filename,temppath));
|
||||||
|
|
||||||
|
StreamingOutput output = outputStream -> {
|
||||||
|
|
||||||
|
String docID;
|
||||||
|
if (recordSet.next()){
|
||||||
|
|
||||||
|
docID = wfToDocBiz.getWfDocPath(workflowid, requestid);
|
||||||
|
log.info("文档生成路径=="+docID);
|
||||||
|
ImageFileManager imageFileManager = new ImageFileManager();
|
||||||
|
int imageFileId = this.getImageFileId(requestid);
|
||||||
|
log.info("imageFileId ==" + imageFileId);
|
||||||
|
InputStream is = imageFileManager.getInputStreamById(this.getImageFileId(requestid));
|
||||||
|
|
||||||
|
String fileName = this.getImageFileName(requestid);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//关闭流
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getImageFileName(String requestid) {
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>获得文件保存目录</h2>
|
||||||
|
* @param
|
||||||
|
* @return String
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
public String getFileSavePath() {
|
||||||
|
SystemComInfo syscominfo = new SystemComInfo();
|
||||||
|
String createdir = FileUpload.getCreateDir(syscominfo.getFilesystem());
|
||||||
|
return createdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>根据requestId返回docimagefile表中imagefileId</h2>
|
||||||
|
* @param requestid
|
||||||
|
* @return int
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
public int getImageFileId(String requestid){
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String get_imagefileid_sql = "select tablename \n" +
|
||||||
|
"from workflow_bill \n" +
|
||||||
|
"where id = (select formid from workflow_base where id = \n" +
|
||||||
|
"(select workflowid from workflow_requestbase where requestid = ?)\n" +
|
||||||
|
")";
|
||||||
|
boolean b = recordSet.executeQuery(get_imagefileid_sql, requestid);
|
||||||
|
log.info("b是否执行"+b);
|
||||||
|
boolean next = recordSet.next();
|
||||||
|
String tablename = recordSet.getString("tablename");
|
||||||
|
String docid_key = this.getWfDocRelateFieldName(requestid);//每一流程表单中用来存放docid的字段名称
|
||||||
|
|
||||||
|
String get_docid_sql = "select "+docid_key+" from "+tablename+" where requestid = ?";
|
||||||
|
boolean b1 = recordSet.executeQuery(get_docid_sql, requestid);
|
||||||
|
log.info("b1是否执行=="+b1);
|
||||||
|
recordSet.next();
|
||||||
|
String docid = recordSet.getString(docid_key);
|
||||||
|
if (Util.null2String(docid).equals("")){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
String get_imagefileid = "select imagefileid from DocImageFile where docid = ? order by versionId";
|
||||||
|
boolean b2 = recordSet.executeQuery(get_imagefileid, docid);
|
||||||
|
log.info("b2是否执行=="+b2);
|
||||||
|
recordSet.next();
|
||||||
|
String imagefileid = recordSet.getString("imagefileid");
|
||||||
|
return Integer.parseInt(imagefileid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据requestId反查 文档关联到的表单字段
|
||||||
|
* @param requestid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getWfDocRelateFieldName(String requestid){
|
||||||
|
String get_wfdocrelatefilename_sql = "select fieldname from workflow_billfield \n" +
|
||||||
|
"where id =(select wfdocrelatefieldid from workflow_base \n" +
|
||||||
|
"where id = (select workflowid from workflow_requestbase where requestid = ?))";
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
boolean b = recordSet.executeQuery(get_wfdocrelatefilename_sql, requestid);
|
||||||
|
log.info("getWfDocRelateFieldName---b是否执行=="+b);
|
||||||
|
boolean next = recordSet.next();
|
||||||
|
String fieldname = recordSet.getString("fieldname");
|
||||||
|
return Util.null2String(fieldname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>得到docfiles 文档附件 在线表单/离线表单(HTML)/离线表单(PDF)</h2>
|
||||||
|
* @param workflowid
|
||||||
|
* @return String
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
public String getDocFiles(String workflowid){
|
||||||
|
RecordSet rs = new RecordSet();
|
||||||
|
String docfiles = "";
|
||||||
|
String wfdocpath = "";
|
||||||
|
rs.executeQuery("select docfiles,wfdocpath from workflow_base where id = ?",workflowid);
|
||||||
|
if (rs.next()){
|
||||||
|
docfiles = weaver.general.Util.null2String(rs.getString("docfiles"));
|
||||||
|
wfdocpath = weaver.general.Util.null2String(rs.getString("wfdocpath"));
|
||||||
|
}
|
||||||
|
if ("".equals(docfiles)&&!"".equals(wfdocpath)){
|
||||||
|
docfiles="1";
|
||||||
|
rs.executeUpdate("update workflow_base set docfiles ='1' where id = ?",workflowid);
|
||||||
|
}
|
||||||
|
return docfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>得到keepsign 是否保留签字意见</h2>
|
||||||
|
* @param workflowid
|
||||||
|
* @return int
|
||||||
|
* @author jiacheng.deng
|
||||||
|
*/
|
||||||
|
public int getKeepSign(String workflowid){
|
||||||
|
RecordSet rs = new RecordSet();
|
||||||
|
int keepsign = 0;
|
||||||
|
rs.executeQuery("select keepsign from workflow_base where id = ?",workflowid);
|
||||||
|
if (rs.next()){
|
||||||
|
keepsign = rs.getInt("keepsign");
|
||||||
|
}
|
||||||
|
return keepsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.mapper;
|
||||||
|
|
||||||
|
import aiyh.utils.annotation.recordset.Select;
|
||||||
|
import aiyh.utils.annotation.recordset.SqlMapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@SqlMapper
|
||||||
|
public interface DetailDataExportExcelMapper{
|
||||||
|
|
||||||
|
@Select("select * from formtable_main_378 fm " +
|
||||||
|
"inner join formtable_main_378_dt1 fmdt " +
|
||||||
|
"on fm.id = fmdt.mainid where fm.requestid = #{requestid}")
|
||||||
|
List<Map<String, Object>> selectTotalData(String requestId);
|
||||||
|
|
||||||
|
// @Select("select * from formtable_main_35 fm " +
|
||||||
|
// "inner join formtable_main_35_dt1 fmdt " +
|
||||||
|
// "on fm.id = fmdt.mainid where fm.requestid = #{requestid}")
|
||||||
|
// List<Map<String, Object>> selectTotalData(String requestId);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.service;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成excel接口文档
|
||||||
|
*/
|
||||||
|
public interface DetailDataExportExcelApi {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于生成最终的excel表格
|
||||||
|
*/
|
||||||
|
public Response exportExcel(String requestId,String lsxjd);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,403 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_fengtianfangzhi.mapper.DetailDataExportExcelMapper;
|
||||||
|
import com.api.chaoyang.he.hcy_fengtianfangzhi.service.DetailDataExportExcelApi;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
|
import org.apache.poi.ss.usermodel.CellType;
|
||||||
|
import org.apache.poi.ss.usermodel.Font;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFCell;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DetailDataExportExcelService implements DetailDataExportExcelApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志
|
||||||
|
*/
|
||||||
|
public Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql
|
||||||
|
*/
|
||||||
|
private DetailDataExportExcelMapper detailDataExportExcelMapper = Util.getMapper(DetailDataExportExcelMapper.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用来返回生成的excel流对象
|
||||||
|
* @param requestId requestid
|
||||||
|
*/
|
||||||
|
public Response exportExcel(String requestId,String lsxjd) {
|
||||||
|
|
||||||
|
//创建一个工作簿
|
||||||
|
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||||
|
//创建一个工作表sheet
|
||||||
|
XSSFSheet sheet = workbook.createSheet();
|
||||||
|
//设置sheet表名称
|
||||||
|
workbook.setSheetName(0, "询价单明细表单");
|
||||||
|
//设置表单列宽
|
||||||
|
sheet.autoSizeColumn(1, true);
|
||||||
|
|
||||||
|
RecordSet recordSet = new RecordSet();//创建sql执行对象
|
||||||
|
|
||||||
|
List<Map<String,Object>> totalDataList = detailDataExportExcelMapper.selectTotalData(requestId);
|
||||||
|
for (Map<String, Object> map : totalDataList) {
|
||||||
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object v = entry.getValue();
|
||||||
|
if (key.equals("sqrxm")){//申请人姓名
|
||||||
|
String get_sqrxm_sql = "select lastname from hrmresource where id=?";
|
||||||
|
recordSet.executeQuery(get_sqrxm_sql,Util.null2String(v));
|
||||||
|
recordSet.next();
|
||||||
|
map.put("sqrxm",recordSet.getString("lastname"));
|
||||||
|
}
|
||||||
|
if (key.equals("gys")){//供应商
|
||||||
|
String get_gys_sql = "select gysqm from uf_gyszsjxx where id=?";
|
||||||
|
recordSet.executeQuery(get_gys_sql, Util.null2String(v));
|
||||||
|
recordSet.next();
|
||||||
|
map.put("gys",recordSet.getString("gysqm"));
|
||||||
|
}
|
||||||
|
if("xjr".equals(key)){//询价人
|
||||||
|
String get_sqrxm_sql = "select lastname from hrmresource where id=?";
|
||||||
|
recordSet.executeQuery(get_sqrxm_sql,Util.null2String(v));
|
||||||
|
recordSet.next();
|
||||||
|
map.put("xjr",recordSet.getString("lastname"));
|
||||||
|
}
|
||||||
|
if ("ylbm".equals(key) ||"sqrbm".equals(key) || "sqrfb".equals(key) || "xjbm".equals(key)){//依赖部门 //申请人部门 //申请人分部 //询价部门
|
||||||
|
String getYlbm = "select departmentname from HrmDepartment where id = ? ";
|
||||||
|
recordSet.executeQuery(getYlbm,Util.null2String(v));
|
||||||
|
recordSet.next();
|
||||||
|
map.put(key,recordSet.getString("departmentname"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("lsxjd".equals(key)){//历史询价单
|
||||||
|
String getLsxjd = "select requestname from workflow_requestbase where requestid = ? ";
|
||||||
|
recordSet.executeQuery(getLsxjd,Util.null2String(v));
|
||||||
|
recordSet.next();
|
||||||
|
map.put(key,recordSet.getString("requestname"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if("sqrbm".equals(key)){//申请人部门
|
||||||
|
// String getYlbm = "select departmentname from HrmDepartment where id = ? ";
|
||||||
|
// recordSet.executeQuery(getYlbm,Util.null2String(v));
|
||||||
|
// recordSet.next();
|
||||||
|
// map.put(key,recordSet.getString("departmentname"));
|
||||||
|
// }
|
||||||
|
// if ("sqrfb".equals(key)){//申请人分部
|
||||||
|
// String getYlbm = "select departmentname from HrmDepartment where id = ? ";
|
||||||
|
// recordSet.executeQuery(getYlbm,Util.null2String(v));
|
||||||
|
// recordSet.next();
|
||||||
|
// map.put(key,recordSet.getString("departmentname"));
|
||||||
|
// }
|
||||||
|
// if("xjbm".equals(key)){//询价部门
|
||||||
|
// String getYlbm = "select departmentname from HrmDepartment where id = ? ";
|
||||||
|
// recordSet.executeQuery(getYlbm,Util.null2String(v));
|
||||||
|
// recordSet.next();
|
||||||
|
// map.put(key,recordSet.getString("departmentname"));
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String getMainTableValueSql = "select id from uf_xjdmxbdczdpzbd where wybs = 'ftfz_exportExcel' ";
|
||||||
|
boolean b = recordSet.executeQuery(getMainTableValueSql);
|
||||||
|
logger.info("getMainTableValueSql是否执行=="+b);
|
||||||
|
String mainId = "";
|
||||||
|
if (recordSet.next()){
|
||||||
|
mainId = recordSet.getString("id");
|
||||||
|
}
|
||||||
|
String getDetailTableValueSql = "select * from uf_xjdmxbdczdpzbd_dt1 where mainId = ?";
|
||||||
|
boolean b1 = recordSet.executeQuery(getDetailTableValueSql, mainId);
|
||||||
|
logger.info("getDetailTableValueSql是否执行==="+b1);
|
||||||
|
List<String> fieldNamesList = new ArrayList<>();//用于存放excel第一个行的明细表显示名
|
||||||
|
List<String> dBNamesList = new ArrayList<>();//用于存放明细表数据库名
|
||||||
|
|
||||||
|
while (recordSet.next()){
|
||||||
|
String mxzdxsm = recordSet.getString("mxzdxsm");//字段显示名
|
||||||
|
String mxzdsjkm = recordSet.getString("mxzdsjkm");//字段数据库名
|
||||||
|
fieldNamesList.add(mxzdxsm);
|
||||||
|
dBNamesList.add(mxzdsjkm);
|
||||||
|
}
|
||||||
|
logger.info("fieldNamesList=="+fieldNamesList);
|
||||||
|
|
||||||
|
|
||||||
|
XSSFRow row = sheet.createRow(0);
|
||||||
|
|
||||||
|
for (int colNum=0;colNum<fieldNamesList.size();colNum++){
|
||||||
|
XSSFCell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(fieldNamesList.get(colNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (int cowNum=1;cowNum<totalDataList.size()+1;cowNum++){
|
||||||
|
XSSFRow row1 = sheet.createRow(cowNum);
|
||||||
|
Map<String, Object> map1 = totalDataList.get(cowNum-1);
|
||||||
|
|
||||||
|
for (int colNum=0;colNum<dBNamesList.size();colNum++){
|
||||||
|
XSSFCell cell = row1.createCell(colNum);
|
||||||
|
String field = dBNamesList.get(colNum);
|
||||||
|
|
||||||
|
if (!"".equals(field)){
|
||||||
|
if (field.equals("pl")){//品类---下拉框 :28-消耗品、29-工事、30-劳保用品、31-IT
|
||||||
|
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(Util.null2String(map1.get(field)));
|
||||||
|
|
||||||
|
}else if (field.equals("sqsl")){//申请数量--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if (field.equals("bz")){//币种--浏览按钮
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
if (String.valueOf(o).equals("1")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("RMB");
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if (field.equals("wsdj")){//未税单价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("wszj")){//未税总价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("sl")){//税率(%)--整数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
int value = Integer.parseInt(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("se")){//税额--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("hsdj")){//含税单价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
}else if (field.equals("hszj")){//含税总价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
}else if (field.equals("ddhfqwnqt")){//调达回复期望纳期(天)--整数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
int value = Integer.parseInt(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("chzx")){//存货属性--下拉框:32-临时品、33-定期品
|
||||||
|
if (map1.get(field).equals("0")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("临时品");
|
||||||
|
}else if (map1.get(field).equals("1")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("定期品");
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
}
|
||||||
|
}else if (field.equals("chbm")){//存货编码--自定义单选
|
||||||
|
String get_chbm_sql = "select chbm from uf_chtz_tbtj where id=?";
|
||||||
|
RecordSet recordSet1 = new RecordSet();
|
||||||
|
boolean b4 = recordSet1.executeQuery(get_chbm_sql, map1.get(field));
|
||||||
|
logger.info("get_chbm_sql语句是否查询成功==="+b4);
|
||||||
|
if (recordSet1.next()){
|
||||||
|
String chbmValue = recordSet1.getString("chbm");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(chbmValue);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(String.valueOf(map1.get(field)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.info("NumberFormatException==="+e);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamingOutput output;
|
||||||
|
Response build;
|
||||||
|
Response.ResponseBuilder header = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream inputStream = workbookConvertorStream(workbook);
|
||||||
|
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||||
|
output = outputStream -> {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
header = Response.ok(output,MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.type("application/xlsx")
|
||||||
|
.header("Content-Disposition",
|
||||||
|
"attachment; filename=\""+ new String("询价单明细表单.xlsx".getBytes("GBK"), StandardCharsets.ISO_8859_1)+"\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.error("导出excel错误:" + Util.getErrString(e));
|
||||||
|
}
|
||||||
|
build = header.build();
|
||||||
|
logger.info("走到导出excel这一步");
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字体样式,并设置字体加粗
|
||||||
|
*
|
||||||
|
* @param workbook workbook对象
|
||||||
|
* @return CellStyle对象
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook, Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook worKbook对象
|
||||||
|
* @return CellStyle 字体样式
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SXSSFWorkbook 转 InputStream
|
||||||
|
* @param workbook workbook对象
|
||||||
|
* @return inputStream流
|
||||||
|
*/
|
||||||
|
public static InputStream workbookConvertorStream(Workbook workbook) {
|
||||||
|
InputStream in = null;
|
||||||
|
try{
|
||||||
|
//临时缓冲区
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
//创建临时文件
|
||||||
|
workbook.write(out);
|
||||||
|
byte [] bookByteAry = out.toByteArray();
|
||||||
|
in = new ByteArrayInputStream(bookByteAry);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,497 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.service.impl;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
|
||||||
|
import com.api.chaoyang.he.hcy_fengtianfangzhi.service.DetailDataExportExcelApi;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
|
import org.apache.poi.ss.usermodel.CellType;
|
||||||
|
import org.apache.poi.ss.usermodel.Font;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFCell;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DetailDataExportExcelService_copy implements DetailDataExportExcelApi {
|
||||||
|
|
||||||
|
|
||||||
|
public Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用来返回生成的excel流对象
|
||||||
|
* @param requestId requestid
|
||||||
|
*/
|
||||||
|
public Response exportExcel(String requestId,String lsxjd) {
|
||||||
|
|
||||||
|
//创建一个工作簿
|
||||||
|
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||||
|
//创建一个工作表sheet
|
||||||
|
XSSFSheet sheet = workbook.createSheet();
|
||||||
|
//设置sheet表名称
|
||||||
|
workbook.setSheetName(0, "询价单明细表单");
|
||||||
|
//设置表单列宽
|
||||||
|
sheet.autoSizeColumn(1, true);
|
||||||
|
|
||||||
|
RecordSet recordSet = new RecordSet();//创建sql执行对象
|
||||||
|
String getMainIdSql = "select * from formtable_main_378 where requestId = ?";
|
||||||
|
// String getMainIdSql = "select * from formtable_main_35 where requestId = ?";
|
||||||
|
|
||||||
|
boolean b2 = recordSet.executeQuery(getMainIdSql, requestId);
|
||||||
|
logger.info("getMainIdSql语句是否执行==="+b2);
|
||||||
|
String formtable_main_35_id="";//询价单流程表单id,作为明细表的mianId使用
|
||||||
|
Map<String, Object> mainDataMap = new HashMap<>();//用于存放主表的数据
|
||||||
|
if (recordSet.next()){
|
||||||
|
formtable_main_35_id = recordSet.getString("id");
|
||||||
|
String sqdh = recordSet.getString("sqdh");//申请单号
|
||||||
|
mainDataMap.put("sqdh",sqdh);
|
||||||
|
String sqrxmID = recordSet.getString("sqrxm");//申请人姓名
|
||||||
|
String get_sqrxm_sql = "select lastname from hrmresource where id=?";
|
||||||
|
RecordSet recordSet1 = new RecordSet();
|
||||||
|
recordSet1.executeQuery(get_sqrxm_sql, sqrxmID);
|
||||||
|
if (recordSet1.next()){
|
||||||
|
String lastname = recordSet1.getString("lastname");
|
||||||
|
mainDataMap.put("sqrxm",lastname);
|
||||||
|
}
|
||||||
|
String sqrbm = recordSet.getString("sqrbm");//申请人部门
|
||||||
|
mainDataMap.put("sqrbm",sqrbm);
|
||||||
|
String sqrfb = recordSet.getString("sqrfb");//申请人分部
|
||||||
|
mainDataMap.put("sqrfb",sqrfb);
|
||||||
|
String sqrq = recordSet.getString("sqrq");//申请日期
|
||||||
|
mainDataMap.put("sqrq",sqrq);
|
||||||
|
// String lsxjd = recordSet.getString("lsxjd");//历史询价单
|
||||||
|
mainDataMap.put("lsxjd",lsxjd);
|
||||||
|
String xjbm = recordSet.getString("xjbm");//询价部门
|
||||||
|
mainDataMap.put("xjbm",xjbm);
|
||||||
|
String bz = recordSet.getString("bz");//备注
|
||||||
|
mainDataMap.put("bz",bz);
|
||||||
|
String bjfj = recordSet.getString("bjfj");//报价附件
|
||||||
|
mainDataMap.put("bjfj",bjfj);
|
||||||
|
String yafj = recordSet.getString("yafj");//议案附件
|
||||||
|
mainDataMap.put("yafj",yafj);
|
||||||
|
String sysfj = recordSet.getString("sysfj");//式样书附件
|
||||||
|
mainDataMap.put("sysfj",sysfj);
|
||||||
|
String hszje = recordSet.getString("hszje");//含税总金额
|
||||||
|
mainDataMap.put("hszje",hszje);
|
||||||
|
String wszje = recordSet.getString("wszje");//未税总金额
|
||||||
|
mainDataMap.put("wszje",wszje);
|
||||||
|
String fkblhj = recordSet.getString("fkblhj");//付款比例(合计)
|
||||||
|
mainDataMap.put("fkblhj",fkblhj);
|
||||||
|
String ylbmId = recordSet.getString("ylbm");//依赖部门
|
||||||
|
String getYlbm = "select departmentname from HrmDepartment where id = ? ";
|
||||||
|
recordSet1.executeQuery(getYlbm,ylbmId);
|
||||||
|
recordSet1.next();
|
||||||
|
String departmentname = recordSet1.getString("departmentname");
|
||||||
|
mainDataMap.put("ylbm",departmentname);
|
||||||
|
String xjrid = recordSet.getString("xjr");//询价人
|
||||||
|
recordSet1.executeQuery(get_sqrxm_sql,xjrid);
|
||||||
|
recordSet1.next();
|
||||||
|
String xjr = recordSet1.getString("lastname");
|
||||||
|
mainDataMap.put("xjr",xjr);
|
||||||
|
String gysId = recordSet.getString("gys");//供应商
|
||||||
|
String get_gys_sql = "select gysqm from uf_gyszsjxx where id=?";
|
||||||
|
recordSet1.executeQuery(get_gys_sql, gysId);
|
||||||
|
if (recordSet1.next()){
|
||||||
|
String gysqm = recordSet1.getString("gysqm");
|
||||||
|
mainDataMap.put("gys",gysqm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
logger.info("mainDataMap主表数据==="+mainDataMap);
|
||||||
|
String getDetailDataSql = "select * from formtable_main_378_dt1 where mainId = ?";
|
||||||
|
// String getDetailDataSql = "select * from formtable_main_35_dt1 where mainId = ?";
|
||||||
|
boolean b3 = recordSet.executeQuery(getDetailDataSql, formtable_main_35_id);
|
||||||
|
logger.info("getDetailDataSql语句是否执行==="+b3);
|
||||||
|
List<Map<String,Object>> listMap = new ArrayList<>();//用于存放明细表数据
|
||||||
|
while (recordSet.next()){
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
String id = recordSet.getString("id");
|
||||||
|
map.put("id",id);
|
||||||
|
String mainId1 = recordSet.getString("mainId");
|
||||||
|
map.put("mainId",mainId1);
|
||||||
|
String pl = Util.null2String(recordSet.getString("pl"));
|
||||||
|
String plValue = "";
|
||||||
|
if (!"".equals(pl)){
|
||||||
|
//消耗品 设备工事 劳保用品 IT
|
||||||
|
switch(pl){
|
||||||
|
case "0":
|
||||||
|
plValue = "消耗品";
|
||||||
|
break;
|
||||||
|
case "1" :
|
||||||
|
plValue = "设备工事";
|
||||||
|
break;
|
||||||
|
case "2" :
|
||||||
|
plValue = "劳保用品";
|
||||||
|
break;
|
||||||
|
case "3":
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
plValue = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
map.put("pl", "");
|
||||||
|
}
|
||||||
|
map.put("pl", plValue);
|
||||||
|
String pm = recordSet.getString("pm");
|
||||||
|
map.put("pm",pm);
|
||||||
|
String pp = recordSet.getString("pp");
|
||||||
|
map.put("pp",pp);
|
||||||
|
String ggxh = recordSet.getString("ggxh");
|
||||||
|
map.put("ggxh",ggxh);
|
||||||
|
String jldw = recordSet.getString("jldw");
|
||||||
|
map.put("jldw",jldw);
|
||||||
|
String sqsl = recordSet.getString("sqsl");
|
||||||
|
map.put("sqsl",sqsl);
|
||||||
|
String bz = recordSet.getString("bz");
|
||||||
|
map.put("bz",bz);
|
||||||
|
String wsdj = recordSet.getString("wsdj");
|
||||||
|
map.put("wsdj",wsdj);
|
||||||
|
String wszj = recordSet.getString("wszj");
|
||||||
|
map.put("wszj",wszj);
|
||||||
|
String sl = recordSet.getString("sl");
|
||||||
|
map.put("sl",sl);
|
||||||
|
String se = recordSet.getString("se");
|
||||||
|
map.put("se",se);
|
||||||
|
String hsdj = recordSet.getString("hsdj");
|
||||||
|
map.put("hsdj",hsdj);
|
||||||
|
String hszj = recordSet.getString("hszj");
|
||||||
|
map.put("hszj",hszj);
|
||||||
|
String qwnq = recordSet.getString("qwnq");
|
||||||
|
map.put("qwnq",qwnq);
|
||||||
|
String ddhfqwnqt = recordSet.getString("ddhfqwnqt");
|
||||||
|
map.put("ddhfqwnqt",ddhfqwnqt);
|
||||||
|
String gys = recordSet.getString("gys");
|
||||||
|
map.put("gys",gys);
|
||||||
|
String cx = recordSet.getString("cx");
|
||||||
|
map.put("cx",cx);
|
||||||
|
String jd = recordSet.getString("jd");
|
||||||
|
map.put("jd",jd);
|
||||||
|
String bz1 = recordSet.getString("bz1");
|
||||||
|
map.put("bz1",bz1);
|
||||||
|
String fj = recordSet.getString("fj");
|
||||||
|
map.put("fj",fj);
|
||||||
|
String chzx = recordSet.getString("chzx");
|
||||||
|
map.put("chzx",chzx);
|
||||||
|
String chbm = recordSet.getString("chbm");
|
||||||
|
map.put("chbm",chbm);
|
||||||
|
listMap.add(map);
|
||||||
|
}
|
||||||
|
logger.info("listMap明细表数据==="+listMap);
|
||||||
|
String getMainTableValueSql = "select id from uf_xjdmxbdczdpzbd where wybs = 'ftfz_exportExcel' ";
|
||||||
|
boolean b = recordSet.executeQuery(getMainTableValueSql);
|
||||||
|
logger.info("getMainTableValueSql是否执行=="+b);
|
||||||
|
String mainId = "";
|
||||||
|
if (recordSet.next()){
|
||||||
|
mainId = recordSet.getString("id");
|
||||||
|
}
|
||||||
|
String getDetailTableValueSql = "select * from uf_xjdmxbdczdpzbd_dt1 where mainId = ?";
|
||||||
|
boolean b1 = recordSet.executeQuery(getDetailTableValueSql, mainId);
|
||||||
|
logger.info("getDetailTableValueSql是否执行==="+b1);
|
||||||
|
List<String> fieldNamesList = new ArrayList<>();//用于存放excel第一个行的明细表显示名
|
||||||
|
List<String> mainFieldNamesList = new ArrayList<>();//用于存放excel第一个行的主表显示名
|
||||||
|
List<String> detalsDBName = new ArrayList<>();//用于存放明细表数据库名
|
||||||
|
List<String> mainDBName = new ArrayList<>();//用于存放主表数据库字段名
|
||||||
|
while (recordSet.next()){
|
||||||
|
String sjly = recordSet.getString("sjly");//数据来源
|
||||||
|
String mxzdxsm = recordSet.getString("mxzdxsm");//字段显示名
|
||||||
|
// String mxzdlx = recordSet.getString("mxzdlx");//字段类型
|
||||||
|
String mxzdsjkm = recordSet.getString("mxzdsjkm");//字段数据库名
|
||||||
|
if (!"".equals(sjly)&&sjly.equals("0")){
|
||||||
|
mainFieldNamesList.add(mxzdxsm);
|
||||||
|
mainDBName.add(mxzdsjkm);
|
||||||
|
}else if (!"".equals(sjly)&&sjly.equals("1")){
|
||||||
|
fieldNamesList.add(mxzdxsm);
|
||||||
|
detalsDBName.add(mxzdsjkm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
logger.info("fieldNamesList=="+fieldNamesList);
|
||||||
|
logger.info("mainFieldNamesList=="+mainFieldNamesList);
|
||||||
|
logger.info("detalsDBName=="+detalsDBName);
|
||||||
|
logger.info("mainDBName=="+mainDBName);
|
||||||
|
|
||||||
|
XSSFRow row = sheet.createRow(0);
|
||||||
|
for (int colNum=0;colNum<mainFieldNamesList.size();colNum++){
|
||||||
|
XSSFCell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(mainFieldNamesList.get(colNum));
|
||||||
|
}
|
||||||
|
for (int colNum=mainFieldNamesList.size();colNum<fieldNamesList.size()+mainFieldNamesList.size();colNum++){
|
||||||
|
XSSFCell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(fieldNamesList.get(colNum-mainFieldNamesList.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (int cowNum=1;cowNum<listMap.size()+1;cowNum++){
|
||||||
|
XSSFRow row1 = sheet.createRow(cowNum);
|
||||||
|
Map<String, Object> map1 = listMap.get(cowNum-1);
|
||||||
|
for (int colMainDataNum=0;colMainDataNum<mainDBName.size();colMainDataNum++){
|
||||||
|
XSSFCell cell = row1.createCell(colMainDataNum);
|
||||||
|
String value = mainDBName.get(colMainDataNum);
|
||||||
|
if (!"".equals(value)){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(String.valueOf(mainDataMap.get(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int colNum=mainDBName.size();colNum<detalsDBName.size()+mainDBName.size();colNum++){
|
||||||
|
XSSFCell cell = row1.createCell(colNum);
|
||||||
|
String field = detalsDBName.get(colNum-mainDBName.size());
|
||||||
|
|
||||||
|
if (!"".equals(field)){
|
||||||
|
if (field.equals("pl")){//品类---下拉框 :28-消耗品、29-工事、30-劳保用品、31-IT
|
||||||
|
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(Util.null2String(map1.get(field)));
|
||||||
|
|
||||||
|
}else if (field.equals("sqsl")){//申请数量--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if (field.equals("bz")){//币种--浏览按钮
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
if (String.valueOf(o).equals("1")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("RMB");
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if (field.equals("wsdj")){//未税单价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("wszj")){//未税总价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("sl")){//税率(%)--整数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
int value = Integer.parseInt(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("se")){//税额--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("hsdj")){//含税单价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
}else if (field.equals("hszj")){//含税总价--浮点数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
double value = Double.parseDouble(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
cell.setCellValue("");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
}
|
||||||
|
}else if (field.equals("ddhfqwnqt")){//调达回复期望纳期(天)--整数
|
||||||
|
Object o = map1.get(field);
|
||||||
|
if (!String.valueOf(o).equals("")){
|
||||||
|
int value = Integer.parseInt(String.valueOf(o));
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else if (field.equals("chzx")){//存货属性--下拉框:32-临时品、33-定期品
|
||||||
|
if (map1.get(field).equals("0")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("临时品");
|
||||||
|
}else if (map1.get(field).equals("1")){
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue("定期品");
|
||||||
|
}else {
|
||||||
|
cell.setCellValue("");
|
||||||
|
}
|
||||||
|
}else if (field.equals("chbm")){//存货编码--自定义单选
|
||||||
|
String get_chbm_sql = "select chbm from uf_chtz_tbtj where id=?";
|
||||||
|
RecordSet recordSet1 = new RecordSet();
|
||||||
|
boolean b4 = recordSet1.executeQuery(get_chbm_sql, map1.get(field));
|
||||||
|
logger.info("get_chbm_sql语句是否查询成功==="+b4);
|
||||||
|
if (recordSet1.next()){
|
||||||
|
String chbmValue = recordSet1.getString("chbm");
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(chbmValue);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook));
|
||||||
|
cell.setCellValue(String.valueOf(map1.get(field)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.info("NumberFormatException==="+e);
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamingOutput output;
|
||||||
|
Response build;
|
||||||
|
Response.ResponseBuilder header = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream inputStream = workbookConvertorStream(workbook);
|
||||||
|
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||||
|
output = outputStream -> {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
header = Response.ok(output,MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.type("application/xlsx")
|
||||||
|
.header("Content-Disposition",
|
||||||
|
"attachment; filename=\""+ new String("询价单明细表单.xlsx".getBytes("GBK"), StandardCharsets.ISO_8859_1)+"\"");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.error("导出excel错误:" + Util.getErrString(e));
|
||||||
|
}
|
||||||
|
build = header.build();
|
||||||
|
logger.info("走到导出excel这一步");
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字体样式,并设置字体加粗
|
||||||
|
*
|
||||||
|
* @param workbook workbook对象
|
||||||
|
* @return CellStyle对象
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook, Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook worKbook对象
|
||||||
|
* @return CellStyle 字体样式
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SXSSFWorkbook 转 InputStream
|
||||||
|
* @param workbook workbook对象
|
||||||
|
* @return inputStream流
|
||||||
|
*/
|
||||||
|
public static InputStream workbookConvertorStream(Workbook workbook) {
|
||||||
|
InputStream in = null;
|
||||||
|
try{
|
||||||
|
//临时缓冲区
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
//创建临时文件
|
||||||
|
workbook.write(out);
|
||||||
|
byte [] bookByteAry = out.toByteArray();
|
||||||
|
in = new ByteArrayInputStream(bookByteAry);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
package com.api.chaoyang.he.hcy_fengtianfangzhi.wflistdonestore;
|
||||||
|
|
||||||
|
import aiyh.utils.ApiResult;
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import com.engine.workflow.biz.requestForm.WfToDocBiz;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.tools.zip.ZipEntry;
|
||||||
|
import org.apache.tools.zip.ZipOutputStream;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
import weaver.file.ImageFileManager;
|
||||||
|
import weaver.hrm.HrmUserVarify;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@Path("/hcy_fengtianfangzhi/wflistdonestore")
|
||||||
|
public class ExportPDF {
|
||||||
|
|
||||||
|
private final Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 源码内容
|
||||||
|
*/
|
||||||
|
private final WfToDocBiz wfToDocBiz = new WfToDocBiz();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*已办流程批量导出pdf
|
||||||
|
* @param request 请求
|
||||||
|
* @param response 响应
|
||||||
|
* @return Response 包含所有勾选的压缩包
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/ExportPDF")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response exportPDF(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
String[] split = ids.split(",");
|
||||||
|
String getWorkflowId_sql = "select workflowid from workflow_requestbase where requestid = ?";
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
int amountWorkflowToDoc = this.getAmountWorkflowToDoc(split);//用来统计流程转文档的流程的数量,如果数量为0,返回文件为空
|
||||||
|
StreamingOutput output = outputStream -> {
|
||||||
|
ZipOutputStream zipOut = new ZipOutputStream(outputStream);
|
||||||
|
for (String requestId : split) {
|
||||||
|
boolean b = recordSet.executeQuery(getWorkflowId_sql, requestId);
|
||||||
|
logger.info("b是否执行"+b);
|
||||||
|
String docID;
|
||||||
|
if (recordSet.next()){
|
||||||
|
String workflowid = recordSet.getString("workflowid");
|
||||||
|
docID = wfToDocBiz.getWfDocPath(workflowid, requestId);
|
||||||
|
logger.info("文档生成路径=="+docID);
|
||||||
|
int imageFileId = this.getImageFileId(requestId);
|
||||||
|
if (imageFileId==-1){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
InputStream is = ImageFileManager.getInputStreamById(this.getImageFileId(requestId));
|
||||||
|
String fileName = this.getImageFileName(requestId);
|
||||||
|
if (Util.null2String(fileName).equals("")){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.addToZip(is,zipOut,fileName);//向zipout对象中插入每个pdf文件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//关闭流
|
||||||
|
zipOut.flush();
|
||||||
|
zipOut.close();
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (amountWorkflowToDoc == 0){
|
||||||
|
return Response.ok(ApiResult.error("文件为空"),MediaType.TEXT_PLAIN).build();
|
||||||
|
}
|
||||||
|
String lastname = HrmUserVarify.getUser(request, response).getLastname();
|
||||||
|
String pdfName = lastname + "_" +getCurrentTime() + "_" + split.length;
|
||||||
|
return Response.ok(output, MediaType.APPLICATION_OCTET_STREAM).type("application/zip")
|
||||||
|
.header("Content-Disposition", "attachment;filename=\"" +
|
||||||
|
new String(pdfName.getBytes("GBK"), StandardCharsets.ISO_8859_1) + ".zip" + "\"").build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return Response.ok(ApiResult.error("出现错误,错误原因:"+e),MediaType.TEXT_PLAIN).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当某个流程成功导出pdf时,更新导出状态字段为已导出
|
||||||
|
* @param requestId requestid
|
||||||
|
*/
|
||||||
|
public void updateValue(String requestId){
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String get_imagefileid_sql = "select tablename \n" +
|
||||||
|
"from workflow_bill \n" +
|
||||||
|
"where id = (select formid from workflow_base where id = \n" +
|
||||||
|
"(select workflowid from workflow_requestbase where requestid = ?)\n" +
|
||||||
|
")";
|
||||||
|
boolean b = recordSet.executeQuery(get_imagefileid_sql, requestId);
|
||||||
|
logger.info("updateValue()----b是否执行"+b);
|
||||||
|
recordSet.next();
|
||||||
|
String tablename = recordSet.getString("tablename");
|
||||||
|
boolean b1 = recordSet.executeUpdate("update " + tablename + " set dczt = 0,dcsj = ? where requestid = ?",getCurrentTime(), requestId);
|
||||||
|
logger.info("updatevalue()----b1是否执行==="+b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用来统计流程转文档的流程的数量,如果数量为0,返回文件为空,
|
||||||
|
* 并将已经流程转流程并且导出pdf的流程字段更新相关流程的导出字段为已导出
|
||||||
|
* @param split requestid组成的数组集合
|
||||||
|
* @return int 返回一个整型数字用于判断
|
||||||
|
*/
|
||||||
|
public int getAmountWorkflowToDoc(String[] split){
|
||||||
|
int amount = 0;
|
||||||
|
String getWorkflowId_sql = "select workflowid from workflow_requestbase where requestid = ?";
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
for (String requestId : split) {
|
||||||
|
boolean b = recordSet.executeQuery(getWorkflowId_sql, requestId);
|
||||||
|
logger.info("getAmountWorkflowToDoc----b是否执行==="+b);
|
||||||
|
if (recordSet.next()){
|
||||||
|
int imageFileId = this.getImageFileId(requestId);
|
||||||
|
if (imageFileId==-1){
|
||||||
|
continue;
|
||||||
|
}else {
|
||||||
|
amount++;
|
||||||
|
//用来更新更新具体哪个流程表单的导出状态为已导出
|
||||||
|
this.updateValue(requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据requestId返回docimagefile表中imagefileId
|
||||||
|
* @param requestId requestid
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public int getImageFileId(String requestId){
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String get_imagefileid_sql = "select tablename \n" +
|
||||||
|
"from workflow_bill \n" +
|
||||||
|
"where id = (select formid from workflow_base where id = \n" +
|
||||||
|
"(select workflowid from workflow_requestbase where requestid = ?)\n" +
|
||||||
|
")";
|
||||||
|
boolean b = recordSet.executeQuery(get_imagefileid_sql, requestId);
|
||||||
|
logger.info("b是否执行"+b);
|
||||||
|
recordSet.next();
|
||||||
|
String tablename = recordSet.getString("tablename");
|
||||||
|
String docid_key = this.getWfDocRelateFieldName(requestId);//每一流程表单中用来存放docid的字段名称
|
||||||
|
|
||||||
|
String get_docid_sql = "select "+docid_key+" from "+tablename+" where requestid = ?";
|
||||||
|
boolean b1 = recordSet.executeQuery(get_docid_sql, requestId);
|
||||||
|
logger.info("b1是否执行=="+b1);
|
||||||
|
recordSet.next();
|
||||||
|
String docid = recordSet.getString(docid_key);
|
||||||
|
if (Util.null2String(docid).equals("")){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
String get_imagefileid = "select imagefileid from DocImageFile where docid = ? order by versionId";
|
||||||
|
boolean b2 = recordSet.executeQuery(get_imagefileid, docid);
|
||||||
|
logger.info("b2是否执行=="+b2);
|
||||||
|
recordSet.next();
|
||||||
|
String imagefileid = recordSet.getString("imagefileid");
|
||||||
|
return Integer.parseInt(imagefileid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据requestId返回docimagefile表中imagefilename
|
||||||
|
* @param requestId requestid
|
||||||
|
* @return String 返回imagefilename
|
||||||
|
*/
|
||||||
|
public String getImageFileName(String requestId){
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String get_tablename_sql = "select tablename \n" +
|
||||||
|
"from workflow_bill \n" +
|
||||||
|
"where id = (select formid from workflow_base where id = \n" +
|
||||||
|
"(select workflowid from workflow_requestbase where requestid = ?)\n" +
|
||||||
|
")";
|
||||||
|
boolean b = recordSet.executeQuery(get_tablename_sql, requestId);
|
||||||
|
logger.info("b是否执行"+b);
|
||||||
|
recordSet.next();
|
||||||
|
String tablename = recordSet.getString("tablename");
|
||||||
|
String docid_key = this.getWfDocRelateFieldName(requestId);//每一流程表单中用来存放docid的字段名称
|
||||||
|
|
||||||
|
String get_docid_sql = "select "+docid_key+" from "+tablename+" where requestid = ?";
|
||||||
|
boolean b1 = recordSet.executeQuery(get_docid_sql, requestId);
|
||||||
|
logger.info("b1是否执行=="+b1);
|
||||||
|
recordSet.next();
|
||||||
|
String docid = recordSet.getString(docid_key);
|
||||||
|
String get_imagefilename = "select imagefilename from DocImageFile where docid = ? order by versionId";
|
||||||
|
boolean b2 = recordSet.executeQuery(get_imagefilename, docid);
|
||||||
|
logger.info("b2是否执行=="+b2);
|
||||||
|
recordSet.next();
|
||||||
|
return recordSet.getString("imagefilename");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据requestId反查 文档关联到的表单字段
|
||||||
|
* @param requestid requestid
|
||||||
|
* @return String 返回filename
|
||||||
|
*/
|
||||||
|
public String getWfDocRelateFieldName(String requestid){
|
||||||
|
String get_wfdocrelatefilename_sql = "select fieldname from workflow_billfield \n" +
|
||||||
|
"where id =(select wfdocrelatefieldid from workflow_base \n" +
|
||||||
|
"where id = (select workflowid from workflow_requestbase where requestid = ?))";
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
boolean b = recordSet.executeQuery(get_wfdocrelatefilename_sql, requestid);
|
||||||
|
recordSet.next();
|
||||||
|
logger.info("getWfDocRelateFieldName---b是否执行=="+b);
|
||||||
|
String fieldname = recordSet.getString("fieldname");
|
||||||
|
return Util.null2String(fieldname);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 添加压缩文件
|
||||||
|
* @param is 文件输入流
|
||||||
|
* @param zipOut zipOUtPutStream
|
||||||
|
* @param fileName 文件名称
|
||||||
|
*/
|
||||||
|
private void addToZip(InputStream is, ZipOutputStream zipOut, String fileName) {
|
||||||
|
try{
|
||||||
|
ZipEntry entry = new ZipEntry(fileName);
|
||||||
|
zipOut.putNextEntry(entry);
|
||||||
|
int len;
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while ((len = is.read(buffer)) > 0) {
|
||||||
|
zipOut.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
zipOut.closeEntry();
|
||||||
|
is.close();
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>获取系统当前日期时间</h2>
|
||||||
|
* @return String
|
||||||
|
* @author hcy
|
||||||
|
* 2023/4/4 17:31
|
||||||
|
*/
|
||||||
|
public String getCurrentTime() {
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
return now.format(formatter);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,683 @@
|
||||||
|
package com.api.chaoyang.he.hcy_xintiantongxin;
|
||||||
|
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Path("/hcy_xintiantongxin")
|
||||||
|
public class ExportExcelAVPNApi {
|
||||||
|
|
||||||
|
private Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*导出AVPN Excel表格
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/ExportExcelAVPNApi")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response checkRequestInfo(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
|
|
||||||
|
Response build = null;
|
||||||
|
Response.ResponseBuilder header = null;
|
||||||
|
try {
|
||||||
|
//查询到 uf_avpn_export AVPN导出表中的一条数据的ids
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
RecordSet rs = new RecordSet();
|
||||||
|
StreamingOutput output = null;
|
||||||
|
|
||||||
|
String sql_AVPN_db = "select * from uf_avpn_export where id = ?";
|
||||||
|
rs.executeQuery(sql_AVPN_db, ids);
|
||||||
|
|
||||||
|
//创建一个工作簿
|
||||||
|
Workbook workbook = new XSSFWorkbook();
|
||||||
|
String[] dbFiled = new String[10];
|
||||||
|
String mainId = "";
|
||||||
|
|
||||||
|
while (rs.next()) {
|
||||||
|
//查询到uf_avpn_export表的id作为查询明细表的mainid
|
||||||
|
mainId = rs.getString("id");
|
||||||
|
String pd_nameId = rs.getString("pd_name");//uf_pd_base_inf 得到的pd_name是字段的id
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String getPd_nameByIdSql = "select pd_name from uf_pd_base_inf where id =?";
|
||||||
|
boolean b1 = recordSet.executeQuery(getPd_nameByIdSql, pd_nameId);
|
||||||
|
|
||||||
|
logger.info("查询pd_namesql语句是否成功==" + b1);
|
||||||
|
if (recordSet.next()) {
|
||||||
|
String pd_name = recordSet.getString("pd_name");//查询到pd_name的值
|
||||||
|
dbFiled[0] = pd_name;
|
||||||
|
}
|
||||||
|
String billing_model = rs.getString("billing_model");
|
||||||
|
if (billing_model.equals("0")){
|
||||||
|
billing_model = "Assignment Model Billing in China";
|
||||||
|
}
|
||||||
|
dbFiled[1] = billing_model;
|
||||||
|
String customer_name = rs.getString("customer_name");
|
||||||
|
dbFiled[2] = customer_name;
|
||||||
|
String ecrm_opportunity = rs.getString("ecrm_opportunity");
|
||||||
|
dbFiled[3] = ecrm_opportunity;
|
||||||
|
String igloo = rs.getString("igloo");
|
||||||
|
dbFiled[4] = igloo;
|
||||||
|
String terms = rs.getString("terms");
|
||||||
|
dbFiled[5] = terms;
|
||||||
|
String att_lead_pricer = rs.getString("att_lead_pricer");
|
||||||
|
dbFiled[6] = att_lead_pricer;
|
||||||
|
String requestjustificationoptional = rs.getString("requestjustificationoptional");
|
||||||
|
dbFiled[7] = requestjustificationoptional;
|
||||||
|
String specialrequesttosstoptional = rs.getString("specialrequesttosstoptional");
|
||||||
|
dbFiled[8] = specialrequesttosstoptional;
|
||||||
|
String sst_contact = rs.getString("sst_contact");
|
||||||
|
dbFiled[9] = sst_contact;
|
||||||
|
}
|
||||||
|
//表单字段
|
||||||
|
String[] tableFiled = new String[]{"Service Type", "Billing Model", "Customer Name", "ECRM Opportunity#", "Igloo #", "Contract Term (in months)", "AT&T Lead Pricer", "Request Justification (optional)", "Special Request to SST (Optional)", "SST Contact"};
|
||||||
|
//明细表字段
|
||||||
|
String[] mergerDetailTableFiled = new String[]{"Site ID", "Description", "Bandwidth", "OTC in RMB", "MRC in RMB"};
|
||||||
|
|
||||||
|
//创建一个工作表sheet
|
||||||
|
Sheet sheet = workbook.createSheet();
|
||||||
|
workbook.setSheetName(0, "AVPN表单");
|
||||||
|
sheet.autoSizeColumn(1, true);
|
||||||
|
//写入数据 //写入1-11行第一列 第二列数据
|
||||||
|
for (int rowNum = 1; rowNum < 11; rowNum++) {
|
||||||
|
Row row = sheet.createRow(rowNum);
|
||||||
|
Cell cell0 = row.createCell(0);
|
||||||
|
cell0.setCellStyle(this.setWordStyle(workbook, true));
|
||||||
|
cell0.setCellValue(tableFiled[rowNum - 1]);
|
||||||
|
Cell cell1 = row.createCell(1);
|
||||||
|
cell1.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell1.setCellValue(dbFiled[rowNum - 1]);
|
||||||
|
}
|
||||||
|
Row row12 = sheet.createRow(12);//第13行
|
||||||
|
Cell cell12_11 = row12.createCell(11);
|
||||||
|
cell12_11.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell12_11.setCellValue("Default Assignment Model-Billing in China in RMB");
|
||||||
|
Cell cell12_12 = row12.createCell(12);
|
||||||
|
cell12_12.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(12, 12, 11, 12));
|
||||||
|
|
||||||
|
//设置14行到15行前5列内容
|
||||||
|
Row row13 = sheet.createRow(13);
|
||||||
|
Row row14 = sheet.createRow(14);
|
||||||
|
for (int colNum = 0; colNum < 5; colNum++) {
|
||||||
|
Cell cell = row13.createCell(colNum);
|
||||||
|
cell.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell.setCellValue(mergerDetailTableFiled[colNum]);
|
||||||
|
Cell cell1 = row14.createCell(colNum);
|
||||||
|
cell1.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell1.setCellValue(mergerDetailTableFiled[colNum]);
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(13, 14, colNum, colNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//设置14行第六列到第13列的内容
|
||||||
|
String[] mergerDetailTableFiled02 = new String[]{"Discount Requested (%)", "Discount SST approved (%)", "Net Price to AT&T Customer", "Royalty to SST without Tax(contra-revenue)"};
|
||||||
|
Cell cell13_5 = row13.createCell(5);
|
||||||
|
cell13_5.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_5.setCellValue(mergerDetailTableFiled02[0]);
|
||||||
|
Cell cell113_6 = row13.createCell(6);
|
||||||
|
cell113_6.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell113_6.setCellValue(mergerDetailTableFiled02[0]);
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(13, 13, 5, 6));
|
||||||
|
|
||||||
|
|
||||||
|
Cell cell13_7 = row13.createCell(7);
|
||||||
|
cell13_7.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_7.setCellValue(mergerDetailTableFiled02[1]);
|
||||||
|
Cell cell13_8 = row13.createCell(8);
|
||||||
|
cell13_8.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_8.setCellValue(mergerDetailTableFiled02[1]);
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(13, 13, 7, 8));
|
||||||
|
|
||||||
|
Cell cell13_9 = row13.createCell(9);
|
||||||
|
cell13_9.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_9.setCellValue(mergerDetailTableFiled02[2]);
|
||||||
|
Cell cell13_10 = row13.createCell(10);
|
||||||
|
cell13_10.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_10.setCellValue(mergerDetailTableFiled02[2]);
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(13, 13, 9, 10));
|
||||||
|
|
||||||
|
Cell cell13_11 = row13.createCell(11);
|
||||||
|
cell13_11.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_11.setCellValue(mergerDetailTableFiled02[3]);
|
||||||
|
Cell cell13_12 = row13.createCell(12);
|
||||||
|
cell13_12.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell13_12.setCellValue(mergerDetailTableFiled02[3]);
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(13, 13, 11, 12));
|
||||||
|
|
||||||
|
|
||||||
|
//设置15行第六列到第13列的内容
|
||||||
|
String[] mergerDetailTableFiled03 = new String[]{"OTC", "MRC", "OTC", "MRC", "OTC in RMB", "MRC in RMB", "OTC in RMB", "MRC in RMB"};
|
||||||
|
for (int colNum = 5; colNum < 13; colNum++) {
|
||||||
|
Cell cell = row14.createCell(colNum);
|
||||||
|
for (int num = 0; num < 8; num++) {
|
||||||
|
cell.setCellStyle(setWordStyle(workbook, true));
|
||||||
|
cell.setCellValue(mergerDetailTableFiled03[num]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<Map<String, String>> siteMap = new ArrayList<>();//用来存放site的类型和每个site的状态
|
||||||
|
|
||||||
|
String getSiteSql = "select site ,count(site) siteNum from uf_avpn_export_dt1 where mainId =? group by site ;";
|
||||||
|
RecordSet rs02 = new RecordSet();
|
||||||
|
boolean b = rs02.executeQuery(getSiteSql, mainId);
|
||||||
|
logger.info("查询site,以及site的数量的sql语句===" + b);
|
||||||
|
|
||||||
|
while (rs02.next()) {
|
||||||
|
HashMap<String, String> map = new HashMap<>();
|
||||||
|
String site = rs02.getString("site");
|
||||||
|
String siteNum = rs02.getString("siteNum");
|
||||||
|
map.put(site, siteNum);
|
||||||
|
siteMap.add(map);//将site的类型和每个site的类型的个数放到siteList数组中,用来循环遍历再放入单元格中
|
||||||
|
}
|
||||||
|
int firstCow = 15;//从第15行开始写入明细表,固定死
|
||||||
|
int finalyCow = 0;
|
||||||
|
|
||||||
|
|
||||||
|
Double sum9 = 0D;
|
||||||
|
Double sum10 = 0D;
|
||||||
|
Double sum11 = 0D;
|
||||||
|
Double sum12 = 0D;
|
||||||
|
for (Map<String, String> map : siteMap) {
|
||||||
|
Set<String> siteKeySet = map.keySet();
|
||||||
|
List<List<Object>> detailDateList = new ArrayList();
|
||||||
|
int cowLength = 0;//每一个site类型的数据的长度
|
||||||
|
for (String s1 : siteKeySet) {
|
||||||
|
cowLength = Integer.parseInt(map.get(s1));//每一个site类型的数据的长度
|
||||||
|
String getDetailDataSql = "select * from uf_avpn_export_dt1 where mainId =? and site = ?";
|
||||||
|
rs02.executeQuery(getDetailDataSql, mainId, s1);
|
||||||
|
while (rs02.next()) {
|
||||||
|
String site = rs02.getString("site");//Site ID
|
||||||
|
String itemId = rs02.getString("Item");//Description
|
||||||
|
String getItemValueSql = "select item from uf_pd_item_inf where id =?";
|
||||||
|
RecordSet recordSet1 = new RecordSet();
|
||||||
|
boolean b2 = recordSet1.executeQuery(getItemValueSql, itemId);
|
||||||
|
logger.info("查询item值sql是否执行成功=" + b2);
|
||||||
|
String item = "";//从数据库中查询到item的值
|
||||||
|
if (recordSet1.next()) {
|
||||||
|
item = recordSet1.getString("Item");
|
||||||
|
}
|
||||||
|
|
||||||
|
String categoryId = rs02.getString("Category");//Bandwidth
|
||||||
|
String getCategoryByIdSql = "select category from uf_pd_category_inf where id = ?";
|
||||||
|
boolean b3 = recordSet1.executeQuery(getCategoryByIdSql, categoryId);
|
||||||
|
logger.info("查询categorySql是否执行成功==" + b3);
|
||||||
|
String category = "";
|
||||||
|
if (recordSet1.next()) {
|
||||||
|
category = recordSet1.getString("category");
|
||||||
|
}
|
||||||
|
String otc_price = rs02.getString("otc_price");//OTC in RMB
|
||||||
|
String mrc_price = rs02.getString("mrc_price");//MRC in RMB
|
||||||
|
String otcdiscountrequest = rs02.getString("otcdiscountrequest");//Discount Requested (%) OTC
|
||||||
|
String mrcdiscountrequest = rs02.getString("mrcdiscountrequest");//Discount Requested (%) MRC
|
||||||
|
String otcdiscountapproved = rs02.getString("otcdiscountapproved");//Discount SST approved (%) OTC
|
||||||
|
String mrcdiscountapproved = rs02.getString("mrcdiscountapproved");//Discount SST approved (%)MRC
|
||||||
|
String otc_tariff = rs02.getString("otc_tariff");//Net Price to AT&T Customer(OTC in RMB)
|
||||||
|
String mrc_tariff = rs02.getString("mrc_tariff");//Net Price to AT&T Customer(MRC in RMB)
|
||||||
|
String otc_scale = rs02.getString("otc_scale");//Royalty to SST without Tax (contra-revenue)(OTC in RMB)
|
||||||
|
String mrc_scale = rs02.getString("mrc_scale");//Royalty to SST without Tax (contra-revenue)(MRC in RMB)
|
||||||
|
ArrayList<Object> list = new ArrayList<>();
|
||||||
|
list.add(site);//list.add(site);
|
||||||
|
list.add(item);
|
||||||
|
list.add(category);
|
||||||
|
list.add(otc_price);
|
||||||
|
list.add(mrc_price);
|
||||||
|
list.add(otcdiscountrequest);
|
||||||
|
list.add(mrcdiscountrequest);
|
||||||
|
list.add(otcdiscountapproved);
|
||||||
|
list.add(mrcdiscountapproved);
|
||||||
|
list.add(otc_tariff);
|
||||||
|
list.add(mrc_tariff);
|
||||||
|
list.add(otc_scale);
|
||||||
|
list.add(mrc_scale);
|
||||||
|
detailDateList.add(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Double amount9 = 0D;//用来统计每一个site种类的 Grand-Total(RMB)
|
||||||
|
Double amount10 = 0D;
|
||||||
|
Double amount11 = 0D;
|
||||||
|
Double amount12 = 0D;
|
||||||
|
//得到list类型的数据、有得到每种site类型的数据长度
|
||||||
|
//开始向excel中导入明细表中的数据
|
||||||
|
int i = 0;//用它来增加detailDateList的下标
|
||||||
|
for (int cowNum = firstCow; cowNum < firstCow + cowLength; cowNum++) {
|
||||||
|
Row row = sheet.createRow(cowNum);
|
||||||
|
for (int colNum = 0; colNum < 13; colNum++) {
|
||||||
|
if (colNum == 9) {
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount9 = Double.valueOf(amount9) + value;
|
||||||
|
sum9 += value;
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else{
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
} else if (colNum == 10) {
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount10 = amount10 + value;
|
||||||
|
sum10 += value;
|
||||||
|
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
}
|
||||||
|
} else if (colNum == 11) {
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount11 = amount11 + value;
|
||||||
|
sum11 += value;
|
||||||
|
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
} else if (colNum == 12) {
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount12 = amount12 + value;
|
||||||
|
sum12 += value;
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colNum == 5 || colNum == 6 || colNum == 7 || colNum == 8) {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellValue( (int)(value*100) + "%");
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (colNum == 0||colNum==1||colNum==2){
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
String value = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (value!=""){
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}else if (colNum == 3||colNum==4){
|
||||||
|
String s = String.valueOf(detailDateList.get(i).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellType(CellType.NUMERIC);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
//合并单元格
|
||||||
|
int lastCow = firstCow + cowLength;
|
||||||
|
|
||||||
|
if (cowLength > 1) {
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(firstCow, lastCow - 1, 0, 0));
|
||||||
|
}
|
||||||
|
//开始写入Sub-Total(RMB)
|
||||||
|
Row rowSubTotal = sheet.createRow(lastCow);
|
||||||
|
for (int colNum = 8; colNum < 13; colNum++) {
|
||||||
|
if (colNum == 8) {
|
||||||
|
Cell cellSubToTal = rowSubTotal.createCell(colNum);
|
||||||
|
cellSubToTal.setCellStyle(setNolyWordStyle(workbook, true));
|
||||||
|
cellSubToTal.setCellValue("Sub-Total(RMB)");
|
||||||
|
} else if (colNum == 9) {
|
||||||
|
Cell cellSubToTal = rowSubTotal.createCell(colNum);
|
||||||
|
cellSubToTal.setCellStyle(setNolyWordStyle(workbook, true));
|
||||||
|
cellSubToTal.setCellValue(amount9);
|
||||||
|
} else if (colNum == 10) {
|
||||||
|
Cell cellSubToTal = rowSubTotal.createCell(colNum);
|
||||||
|
cellSubToTal.setCellStyle(setNolyWordStyle(workbook, true));
|
||||||
|
cellSubToTal.setCellValue(amount10);
|
||||||
|
} else if (colNum == 11) {
|
||||||
|
Cell cellSubToTal = rowSubTotal.createCell(colNum);
|
||||||
|
cellSubToTal.setCellStyle(setNolyWordStyle(workbook, true));
|
||||||
|
cellSubToTal.setCellValue(amount11);
|
||||||
|
} else if (colNum == 12) {
|
||||||
|
Cell cellSubToTal = rowSubTotal.createCell(colNum);
|
||||||
|
cellSubToTal.setCellStyle(setNolyWordStyle(workbook, true));
|
||||||
|
cellSubToTal.setCellValue(amount12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
firstCow = firstCow + cowLength + 1;
|
||||||
|
finalyCow = firstCow;
|
||||||
|
}//处理插入数据以及合并单元格
|
||||||
|
//处理明细表最后一行的统计情况
|
||||||
|
Row row_Grand_Total_RMB = sheet.createRow(finalyCow);
|
||||||
|
for (int colNum = 8; colNum < 13; colNum++) {
|
||||||
|
if (colNum == 8) {
|
||||||
|
Cell cell = row_Grand_Total_RMB.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue("Grand-Total(RMB)");
|
||||||
|
} else if (colNum == 9) {
|
||||||
|
Cell cell = row_Grand_Total_RMB.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(sum9);
|
||||||
|
} else if (colNum == 10) {
|
||||||
|
Cell cell = row_Grand_Total_RMB.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(sum10);
|
||||||
|
} else if (colNum == 11) {
|
||||||
|
Cell cell = row_Grand_Total_RMB.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(sum11);
|
||||||
|
} else if (colNum == 12) {
|
||||||
|
Cell cell = row_Grand_Total_RMB.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(sum12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//输入最下面的固定值
|
||||||
|
String[] lastStringLeft = new String[]{
|
||||||
|
"Notes:",
|
||||||
|
"1.This is NOT AT&T Pricing Approval. Forward this SST quote to your Lead ICB Pricer to issue the official Rate Letter",
|
||||||
|
"2.Access price must be quoted by SST, request SST as \"access provider\" in Igloo. Do NOT use quote from other access providers",
|
||||||
|
"3.Discount is only applicable to the demand set listed below, any changes in bandwidth/design/billing model, need to re-submit to SST and AT&T Pricer for approval", "" +
|
||||||
|
"4.Under in-country billing, AT&T must follow the discount% offered by SST. Do NOT deviate from the SST quoted discount.",
|
||||||
|
"5.SST can support repeatable discount for the same solution design(port/CDR/CPE) of same contract term which was approved already. Local Loop part are non-repeatable.",
|
||||||
|
"6.For ICB only - Royalty in columns L and M are contra-revenue,ICB to verify if BCRL captured these amount.",
|
||||||
|
"7.Save this SST approved quote to eCRM (without release) for record.",
|
||||||
|
"8.The quotation shall be exclusive of any applicable taxes and Customer shall pay all the taxes and charges relating to the payment if any. The VAT rate for CPE and related modules and license are 16%, the rest is 6%.",
|
||||||
|
"9.Thank you for considering SST, this engagement may be subject to a credit check, we appreciate your cooperation and look forward to doing business in the near future."
|
||||||
|
};
|
||||||
|
int lastLineTag = finalyCow + 2;//固定值Note所在的行
|
||||||
|
int k = 0;//用来增加lastStringLeft的下标
|
||||||
|
for (int rowNum = lastLineTag; rowNum < lastLineTag + 10; rowNum++) {
|
||||||
|
Row row = sheet.createRow(rowNum);
|
||||||
|
Cell cell = row.createCell(0);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(rowNum, rowNum, 0, 11));
|
||||||
|
cell.setCellStyle(setTotalStyle(workbook,"微软雅黑",true,false,HorizontalAlignment.LEFT, VerticalAlignment.CENTER,false));
|
||||||
|
cell.setCellValue(lastStringLeft[k]);
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
sheet.autoSizeColumn(0,true);
|
||||||
|
sheet.setColumnWidth(0,sheet.getColumnWidth(0)*5/10);
|
||||||
|
// 设置自动列宽
|
||||||
|
for (int num = 1; num < 13; num++) {
|
||||||
|
sheet.autoSizeColumn(num,true);
|
||||||
|
sheet.setColumnWidth(num, (sheet.getColumnWidth(num)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InputStream inputStream = workbookConvertorStream(workbook);
|
||||||
|
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||||
|
output = outputStream -> {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
header = Response.ok(output,MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.type("application/xlsx")
|
||||||
|
.header("Content-Disposition",
|
||||||
|
"attachment; filename=\""+ new String("AVPN表单.xlsx".getBytes("GBK"), StandardCharsets.ISO_8859_1)+"\"");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
logger.error("导出excel错误:" + Util.getErrString(e));
|
||||||
|
}
|
||||||
|
build = header.build();
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <h2>设置每个单元格样式:包含字体类型,字体是否加粗,是否包含边框,元素水平方向的位置,元素垂直方向的位置,是否设置元素的数据格式,</h2>
|
||||||
|
* @param
|
||||||
|
* @return
|
||||||
|
* @author hcy
|
||||||
|
* @Date 2023/2/13 18:08
|
||||||
|
*/
|
||||||
|
public static CellStyle setTotalStyle(Workbook workbook,String type,boolean setBold,boolean setBorder,HorizontalAlignment horizontalDirection,VerticalAlignment verticalAlignmentDirection ,boolean setDataFormat){
|
||||||
|
//new一个cellStyle用于设置每一个单元格样式
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName(type);
|
||||||
|
//设置字体是否加粗
|
||||||
|
font.setBold(setBold);
|
||||||
|
//将字体样式渲染cellStyle对象中
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
if (setBorder == true){
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
}
|
||||||
|
|
||||||
|
//水平居中
|
||||||
|
cellStyle.setAlignment(horizontalDirection);
|
||||||
|
//垂直居中
|
||||||
|
cellStyle.setVerticalAlignment(verticalAlignmentDirection);
|
||||||
|
|
||||||
|
//此处设置数据格式
|
||||||
|
if(setDataFormat == true){
|
||||||
|
DataFormat dataFormat = workbook.createDataFormat();
|
||||||
|
cellStyle.setDataFormat(dataFormat.getFormat("0.00_ "));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 设置每个单元格的样式,并且加粗字体,设置边框
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setWordStyle(Workbook workbook, Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setWordStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置边框样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyBorderStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook,Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
DataFormat dataFormat = workbook.createDataFormat();//此处设置数据格式
|
||||||
|
cellStyle.setDataFormat(dataFormat.getFormat("0.00_ "));
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SXSSFWorkbook 转 InputStream
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static InputStream workbookConvertorStream(Workbook workbook) {
|
||||||
|
InputStream in = null;
|
||||||
|
try{
|
||||||
|
//临时缓冲区
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
//创建临时文件
|
||||||
|
workbook.write(out);
|
||||||
|
byte [] bookByteAry = out.toByteArray();
|
||||||
|
in = new ByteArrayInputStream(bookByteAry);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格格式为数值型
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setCellToNumber(Workbook workbook){
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
DataFormat dataFormat = workbook.createDataFormat();//此处设置数据格式
|
||||||
|
cellStyle.setDataFormat(dataFormat.getFormat("0.00_ "));
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,581 @@
|
||||||
|
package com.api.chaoyang.he.hcy_xintiantongxin;
|
||||||
|
|
||||||
|
import aiyh.utils.Util;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import weaver.conn.RecordSet;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Path("/hcy_xintiantongxin2")
|
||||||
|
public class ExportExcelL3NSApi {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Logger logger = Util.getLogger();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/ExportExcelL3NSApi")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
public Response checkRequestInfo(@Context HttpServletRequest request, @Context HttpServletResponse response){
|
||||||
|
Response build = null;
|
||||||
|
Response.ResponseBuilder header = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
//查询到 uf_L3NS_exportt AVPN导出表中的一条数据的ids
|
||||||
|
String ids = request.getParameter("ids");
|
||||||
|
RecordSet rs = new RecordSet();
|
||||||
|
StreamingOutput output = null;
|
||||||
|
|
||||||
|
|
||||||
|
String sql_L3NS_db = "select * from uf_L3NS_exportt where id = ?";
|
||||||
|
boolean b = rs.executeQuery(sql_L3NS_db, ids);
|
||||||
|
logger.info("是否查询uf_L3NS_exportt=="+b);
|
||||||
|
|
||||||
|
//创建一个工作簿
|
||||||
|
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||||
|
String[] dbFiled = new String[13];
|
||||||
|
String[] tableFiled = new String[]{"Service Type", "Billing Model", "Customer Name", "ROME Opportunity #", "ROME WR#", "Request Type", "Original SST Order NO", "Request Justification", "Special Request to SST", "Target Service Provision date","ATT Order number:","SST Pre-Sales Contact","Date of SST Approval"};
|
||||||
|
|
||||||
|
String mainId = "";
|
||||||
|
|
||||||
|
//得到主表数据,用来写入excel中的固定值
|
||||||
|
while (rs.next()) {
|
||||||
|
//查询到uf_avpn_export表的id作为查询明细表的mainid
|
||||||
|
mainId = rs.getString("id");
|
||||||
|
|
||||||
|
|
||||||
|
String pd_nameId = rs.getString("pd_name");//uf_pd_base_inf 得到的pd_name是字段的id
|
||||||
|
RecordSet recordSet = new RecordSet();
|
||||||
|
String getPd_nameByIdSql = "select pd_name from uf_pd_base_inf where id =?";
|
||||||
|
boolean b1 = recordSet.executeQuery(getPd_nameByIdSql, pd_nameId);
|
||||||
|
|
||||||
|
logger.info("查询pd_namesql语句是否成功==" + b1);
|
||||||
|
if (recordSet.next()) {
|
||||||
|
String pd_name = recordSet.getString("pd_name");//查询到pd_name的值
|
||||||
|
dbFiled[0] = pd_name;
|
||||||
|
}
|
||||||
|
String billingmodel = rs.getString("billingmodel");//Billing Model===billingmodel
|
||||||
|
if (billingmodel.equals("0")){
|
||||||
|
billingmodel = "Custom Subcontract";
|
||||||
|
}else if (billingmodel == null){
|
||||||
|
billingmodel = "";
|
||||||
|
}
|
||||||
|
dbFiled[1] = billingmodel;
|
||||||
|
String customer_name = rs.getString("customer_name");//Customer Name===customer_name
|
||||||
|
dbFiled[2] = customer_name;
|
||||||
|
String rome_opportunity = rs.getString("rome_opportunity");//ROME Opportunity #====rome_opportunity
|
||||||
|
dbFiled[3] = rome_opportunity;
|
||||||
|
String rome_wr = rs.getString("rome_wr");//ROME WR#====rome_wr
|
||||||
|
dbFiled[4] = rome_wr;
|
||||||
|
String request_type = rs.getString("request_type");//Request Type====request_type
|
||||||
|
dbFiled[5] = request_type;
|
||||||
|
String original_sst_order_no = rs.getString("original_sst_order_no");//Original SST Order NO====original_sst_order_no
|
||||||
|
dbFiled[6] = original_sst_order_no;
|
||||||
|
String requestjustificationoptional = rs.getString("requestjustificationoptional");//Request Justification====requestjustificationoptional
|
||||||
|
dbFiled[7] = requestjustificationoptional;
|
||||||
|
String specialrequesttosstoptional = rs.getString("specialrequesttosstoptional");//Special Request to SST====specialrequesttosstoptional
|
||||||
|
dbFiled[8] = specialrequesttosstoptional;
|
||||||
|
String targetserviceprovisiondate = rs.getString("targetserviceprovisiondate");//Target Service Provision date====targetserviceprovisiondate
|
||||||
|
dbFiled[9] = targetserviceprovisiondate;
|
||||||
|
String attordernumber = rs.getString("attordernumber");//ATT Order number:====attordernumber
|
||||||
|
dbFiled[10] = attordernumber;
|
||||||
|
String sstpresalescontact = rs.getString("sstpresalescontact");//SST Pre-Sales Contact====sstpresalescontact
|
||||||
|
dbFiled[11] = sstpresalescontact;
|
||||||
|
String dateofsstapproval = rs.getString("dateofsstapproval");//Date of SST Approval====dateofsstapproval
|
||||||
|
dbFiled[12] = dateofsstapproval;
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始写入固定值
|
||||||
|
Sheet sheet = workbook.createSheet();
|
||||||
|
workbook.setSheetName(0, "L3NS表单");
|
||||||
|
sheet.autoSizeColumn(1, true);
|
||||||
|
|
||||||
|
for (int cowNum=1;cowNum<14;cowNum++){
|
||||||
|
Row row = sheet.createRow(cowNum);
|
||||||
|
Cell cell0 = row.createCell(0);
|
||||||
|
cell0.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell0.setCellValue(tableFiled[cowNum-1]);
|
||||||
|
Cell cell1 = row.createCell(1);
|
||||||
|
cell1.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell1.setCellValue(dbFiled[cowNum-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Row row14 = sheet.createRow(14);//写入15行数据
|
||||||
|
Cell cell14_0 = row14.createCell(0);
|
||||||
|
Cell cell114_1 = row14.createCell(1);
|
||||||
|
cell14_0.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell14_0.setCellValue("This Quote is valid for 180 days from \"Date of SST approval\"");
|
||||||
|
cell114_1.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell114_1.setCellValue("This Quote is valid for 180 days from \"Date of SST approval\"");
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(14, 14, 0, 1));
|
||||||
|
|
||||||
|
|
||||||
|
// 写入18行,19行数据
|
||||||
|
// Site ID Description Bandwidth OTC in RMB MRC in RMB
|
||||||
|
String[] detailFileds = new String[]{"Site ID", "Description","Bandwidth", "OTC in RMB", "MRC in RMB"};
|
||||||
|
Row row17 = sheet.createRow(17);
|
||||||
|
Row row18 = sheet.createRow(18);
|
||||||
|
for (int colNum =0;colNum<5;colNum++){
|
||||||
|
Cell cell = row17.createCell(colNum);
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(detailFileds[colNum]);
|
||||||
|
}
|
||||||
|
for (int colNum =0;colNum<5;colNum++){
|
||||||
|
Cell cell = row18.createCell(colNum);
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
cell.setCellValue(detailFileds[colNum]);
|
||||||
|
}
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 18, 0, 0));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 18, 1, 1));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 18, 2, 2));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 18, 3, 3));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 18, 4, 4));
|
||||||
|
|
||||||
|
// Relief Requested (%) Relief SST approved (%) Tariff to SST (include VAT) 6-11列
|
||||||
|
// OTC MRC OTC MRC OTC in RMB MRC in RMB
|
||||||
|
String[] detailFileds01 = new String[]{"Relief Requested (%)","Relief Requested (%)","Relief SST approved (%)","Relief SST approved (%)","Tariff to SST (include VAT)","Tariff to SST (include VAT)",""};
|
||||||
|
String[] detailFileds02 = new String[]{"OTC","MRC","OTC","MRC","OTC in RMB","MRC in RMB"};
|
||||||
|
int i=0;
|
||||||
|
int j = 0;
|
||||||
|
for(int colNum=5;colNum<11;colNum++){
|
||||||
|
Cell cell17 = row17.createCell(colNum);
|
||||||
|
Cell cell18 = row18.createCell(colNum);
|
||||||
|
cell17.setCellValue(detailFileds01[i]);
|
||||||
|
cell17.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
// cell17.setCellStyle(setCellStyleFrame(workbook));
|
||||||
|
|
||||||
|
cell18.setCellValue(detailFileds02[j]);
|
||||||
|
cell18.setCellStyle(setWordStyle(workbook,true));
|
||||||
|
// cell18.setCellStyle(setCellStyleFrame(workbook));
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
//合并18行,19行
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 17, 5, 6));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 17, 7, 8));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(17, 17, 9, 10));
|
||||||
|
|
||||||
|
|
||||||
|
//开始写入20开始一直往下的动态表格中的内容
|
||||||
|
//
|
||||||
|
|
||||||
|
List<Map<String, String>> siteMap = new ArrayList<>();//用来存放site的类型和每个site的状态
|
||||||
|
String getSiteSql = "select site ,count(site) siteNum from uf_L3NS_exportt_dt1 where mainId =? group by site ;";
|
||||||
|
RecordSet rs02 = new RecordSet();
|
||||||
|
boolean b1 = rs02.executeQuery(getSiteSql, mainId);
|
||||||
|
logger.info("查询site,以及site的数量的sql语句===" + b1);
|
||||||
|
while (rs02.next()) {
|
||||||
|
HashMap<String, String> map = new HashMap<>();
|
||||||
|
String site = rs02.getString("site");
|
||||||
|
String siteNum = rs02.getString("siteNum");
|
||||||
|
map.put(site, siteNum);
|
||||||
|
siteMap.add(map);//将site的类型和每个site的类型的个数放到siteList数组中,用来循环遍历再放入单元格中
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstCow = 19;//从第15行开始写入明细表,固定死
|
||||||
|
int finalyCow = 0;
|
||||||
|
Double sum10 = 0D;//用来统计所有site种类的Grand-Total (RMB) #REF!
|
||||||
|
Double sum9 = 0D;
|
||||||
|
for (Map<String, String> map : siteMap) {
|
||||||
|
|
||||||
|
Set<String> siteKeySet = map.keySet();
|
||||||
|
List<List<Object>> detailDateList = new ArrayList();
|
||||||
|
int cowLength = 0;//每一个site类型的数据的长度
|
||||||
|
for (String s1 : siteKeySet) {
|
||||||
|
cowLength = Integer.parseInt(map.get(s1));//每一个site类型的数据的长度
|
||||||
|
String getDetailDataSql = "select * from uf_L3NS_exportt_dt1 where mainId =? and site = ?";
|
||||||
|
rs02.executeQuery(getDetailDataSql,mainId,s1);
|
||||||
|
while(rs02.next()){
|
||||||
|
//明细表数据
|
||||||
|
String site = rs02.getString("site");//Site ID
|
||||||
|
String itemId = rs02.getString("Item");//Description
|
||||||
|
String getItemValueSql = "select item from uf_pd_item_inf where id =?";//uf_pd_item_inf
|
||||||
|
RecordSet recordSet1 = new RecordSet();
|
||||||
|
boolean b2 = recordSet1.executeQuery(getItemValueSql, itemId);
|
||||||
|
logger.info("查询item值sql是否执行成功=" + b2);
|
||||||
|
String item = "";//从数据库中查询到item的值
|
||||||
|
if (recordSet1.next()) {
|
||||||
|
item = recordSet1.getString("Item");
|
||||||
|
}
|
||||||
|
String categoryId = rs02.getString("Category");//Bandwidth
|
||||||
|
String getCategoryByIdSql = "select category from uf_pd_category_inf where id = ?";//uf_pd_category_inf
|
||||||
|
boolean b3 = recordSet1.executeQuery(getCategoryByIdSql, categoryId);
|
||||||
|
logger.info("查询categorySql是否执行成功==" + b3);
|
||||||
|
String category = "";
|
||||||
|
if (recordSet1.next()) {
|
||||||
|
category = recordSet1.getString("category");
|
||||||
|
}
|
||||||
|
String otc_price = rs02.getString("otc_price");//OTC in RMB
|
||||||
|
String mrc_price = rs02.getString("mrc_price");//MRC in RMB
|
||||||
|
String otc_relief_requested = rs02.getString("otc_relief_requested");//Relief Requested (%)OTC
|
||||||
|
String mrc_relief_requested = rs02.getString("mrc_relief_requested");//Relief Requested (%)MRC
|
||||||
|
String otc_relief_approved = rs02.getString("otc_relief_approved");//Relief SST approved (%)OTC
|
||||||
|
String mrc_relief_approved = rs02.getString("mrc_relief_approved");//Relief SST approved (%)MRC
|
||||||
|
String otc_tariff = rs02.getString("otc_tariff");//Tariff to SST (include VAT)(OTC in RMB)
|
||||||
|
String mrc_tariff = rs02.getString("mrc_tariff");//Tariff to SST (include VAT)(MRC in RMB)
|
||||||
|
|
||||||
|
ArrayList<Object> list = new ArrayList<>();
|
||||||
|
list.add(site);
|
||||||
|
list.add(item);
|
||||||
|
list.add(category);
|
||||||
|
list.add(otc_price);
|
||||||
|
list.add(mrc_price);
|
||||||
|
list.add(otc_relief_requested);
|
||||||
|
list.add(mrc_relief_requested);
|
||||||
|
list.add(otc_relief_approved);
|
||||||
|
list.add(mrc_relief_approved);
|
||||||
|
list.add(otc_tariff);
|
||||||
|
list.add(mrc_tariff);
|
||||||
|
detailDateList.add(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Double amount10 = 0D;//用来统计每一个site种类的 Sub-Total
|
||||||
|
Double amount9 = 0D;
|
||||||
|
|
||||||
|
//得到list类型的数据、有得到每种site类型的数据长度
|
||||||
|
//开始向excel中导入明细表中的数据
|
||||||
|
int k = 0;//用它来增加detailDateList的下标
|
||||||
|
for (int cowNum = firstCow; cowNum < firstCow + cowLength; cowNum++) {
|
||||||
|
Row row = sheet.createRow(cowNum);
|
||||||
|
for (int colNum = 0; colNum < 11; colNum++) {
|
||||||
|
if (colNum == 10) {
|
||||||
|
String s = String.valueOf(detailDateList.get(k).get(colNum));
|
||||||
|
if (s != ""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount10 = amount10 + value;
|
||||||
|
sum10 += value;
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}else if (colNum == 9){
|
||||||
|
String s = String.valueOf(detailDateList.get(k).get(colNum));
|
||||||
|
if (s != "") {
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
amount9 = amount9 + value;
|
||||||
|
sum9 += value;
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (colNum == 5 || colNum == 6 || colNum == 7 || colNum == 8) {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
String s = String.valueOf(detailDateList.get(k).get(colNum));
|
||||||
|
if (s!=""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellValue((int)(value * 100) + "%");
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
} else if (colNum == 3 || colNum==4){
|
||||||
|
String s = String.valueOf(detailDateList.get(k).get(colNum));
|
||||||
|
if (s != ""){
|
||||||
|
Double value = Double.valueOf(s);
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setCellToNumber(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (colNum == 0||colNum==1||colNum==2){
|
||||||
|
Cell cell = row.createCell(colNum);
|
||||||
|
String value = String.valueOf(detailDateList.get(k).get(colNum));
|
||||||
|
if (value!=""){
|
||||||
|
cell.setCellStyle(setWordStyle(workbook));
|
||||||
|
cell.setCellValue(value);
|
||||||
|
}else {
|
||||||
|
cell.setCellStyle(setNolyBorderStyle(workbook));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
//合并单元格
|
||||||
|
int lastCow = firstCow + cowLength;
|
||||||
|
|
||||||
|
if (cowLength > 1) {
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(firstCow, lastCow - 1, 0, 0));
|
||||||
|
}
|
||||||
|
//开始写入Sub-Total(RMB)
|
||||||
|
Row rowSubTotal = sheet.createRow(lastCow);
|
||||||
|
Cell cellSubToTal10 = rowSubTotal.createCell(10);
|
||||||
|
cellSubToTal10.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellSubToTal10.setCellValue(amount10);
|
||||||
|
|
||||||
|
Cell cellSubToTal9 = rowSubTotal.createCell(9);
|
||||||
|
cellSubToTal9.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellSubToTal9.setCellValue(amount9);
|
||||||
|
|
||||||
|
Cell cellSubToTal8 = rowSubTotal.createCell(8);
|
||||||
|
cellSubToTal8.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellSubToTal8.setCellValue("Sub-Total");
|
||||||
|
|
||||||
|
|
||||||
|
firstCow = firstCow + cowLength + 1;
|
||||||
|
finalyCow = firstCow;
|
||||||
|
|
||||||
|
}
|
||||||
|
Row rowFinalyCow = sheet.createRow(finalyCow);
|
||||||
|
Cell cellFinalyCow_10 = rowFinalyCow.createCell(10);
|
||||||
|
cellFinalyCow_10.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellFinalyCow_10.setCellValue(sum10);
|
||||||
|
Cell cellFinalyCow_9 = rowFinalyCow.createCell(9);
|
||||||
|
cellFinalyCow_9.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellFinalyCow_9.setCellValue(sum9);
|
||||||
|
Cell cellFinalyCow_8 = rowFinalyCow.createCell(8);
|
||||||
|
cellFinalyCow_8.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
cellFinalyCow_8.setCellValue("Grand-Total (RMB)");
|
||||||
|
|
||||||
|
|
||||||
|
//输入最下面的固定值
|
||||||
|
String[] lastStringright = new String[]{
|
||||||
|
"Notes:",
|
||||||
|
"1.This form should be submitted by Lead ICB via a ROME WR to \"AP Pricing Team\". Do NOT send this direct to SST.",
|
||||||
|
"2.Fill in full Site Address or at least provide Name of City.",
|
||||||
|
"3.Tariff relief requested should be calculated by lead ICB. Do not fill in target AVPN discount as \"Relief Requested\".",
|
||||||
|
"4.VPN Tariff is port base pricing, no charges on COS or COS profile.",
|
||||||
|
"5.AT&T GAM will place order to SST on no longer than 12 months term for Custom Subcontract, regardless of the commitment term between end customer and AT&T.",
|
||||||
|
"6.China VAT is irrecoverable cost to AT&T under custom subcontracting billing. Ttariff and Access price listed below is VAT inclusive. ICB has to cater for this cost in deal financials.",
|
||||||
|
"7.Tariff Relief is only applicable to the demand set listed below, any changes in bandwidth/design/billing model, need to re-submit to SST for approval;",
|
||||||
|
};
|
||||||
|
int lastLineTag = finalyCow + 2;//固定值Note所在的行
|
||||||
|
int h=0;
|
||||||
|
for (int rowNum=lastLineTag;rowNum<lastLineTag+8;rowNum++){
|
||||||
|
Row row = sheet.createRow(rowNum);
|
||||||
|
Cell cell = row.createCell(0);
|
||||||
|
cell.setCellStyle(setNolyWordStyle(workbook,true));
|
||||||
|
sheet.addMergedRegion(new CellRangeAddress(rowNum, rowNum, 0, 11));
|
||||||
|
cell.setCellValue(lastStringright[h]);
|
||||||
|
h++;
|
||||||
|
}
|
||||||
|
sheet.autoSizeColumn(0,true);
|
||||||
|
sheet.setColumnWidth(0,sheet.getColumnWidth(0)*4/10);
|
||||||
|
// 设置自动列宽
|
||||||
|
for (int num = 1; num < 11; num++) {
|
||||||
|
sheet.autoSizeColumn(num,true);
|
||||||
|
sheet.setColumnWidth(num, (sheet.getColumnWidth(num)));
|
||||||
|
}
|
||||||
|
InputStream inputStream = workbookConvertorStream(workbook);
|
||||||
|
byte[] bytes = IOUtils.toByteArray(inputStream);
|
||||||
|
output = outputStream -> {
|
||||||
|
outputStream.write(bytes);
|
||||||
|
outputStream.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
header = Response.ok(output,MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.type("application/xlsx")
|
||||||
|
.header("Content-Disposition",
|
||||||
|
"attachment; filename=\""+ new String("L3NS表单.xlsx".getBytes("GBK"), StandardCharsets.ISO_8859_1)+"\"");
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("导出excel错误:" + Util.getErrString(e));
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
build = header.build();
|
||||||
|
return build;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置每个单元格的样式,并且加粗字体,设置边框
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setWordStyle(Workbook workbook, Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setWordStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyWordStyle(Workbook workbook,Boolean setBold) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
font.setBold(setBold);
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
DataFormat dataFormat = workbook.createDataFormat();//此处设置数据格式
|
||||||
|
cellStyle.setDataFormat(dataFormat.getFormat("0.00_ "));
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只设置字体样式
|
||||||
|
*
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setNolyBorderStyle(Workbook workbook) {
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SXSSFWorkbook 转 InputStream
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static InputStream workbookConvertorStream(Workbook workbook) {
|
||||||
|
InputStream in = null;
|
||||||
|
try{
|
||||||
|
//临时缓冲区
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
//创建临时文件
|
||||||
|
workbook.write(out);
|
||||||
|
byte [] bookByteAry = out.toByteArray();
|
||||||
|
in = new ByteArrayInputStream(bookByteAry);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格格式为数值型
|
||||||
|
* @param workbook
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static CellStyle setCellToNumber(Workbook workbook){
|
||||||
|
// 设置字体
|
||||||
|
CellStyle cellStyle = workbook.createCellStyle();
|
||||||
|
//设置样式对象,这里仅设置了边框属性
|
||||||
|
cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
|
||||||
|
|
||||||
|
cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
|
||||||
|
|
||||||
|
cellStyle.setBorderTop(BorderStyle.THIN);//上边框
|
||||||
|
|
||||||
|
cellStyle.setBorderRight(BorderStyle.THIN);//右边框
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
//颜色
|
||||||
|
font.setColor(Font.COLOR_NORMAL);
|
||||||
|
//设置字体大小
|
||||||
|
font.setFontHeightInPoints((short) 10);
|
||||||
|
//字体
|
||||||
|
font.setFontName("微软雅黑");
|
||||||
|
cellStyle.setFont(font);
|
||||||
|
DataFormat dataFormat = workbook.createDataFormat();//此处设置数据格式
|
||||||
|
cellStyle.setDataFormat(dataFormat.getFormat("0.00_ "));
|
||||||
|
return cellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue