feat(settle report): download settle report excel from ui

master
yixian 5 years ago
parent 98a8ea0e58
commit 386b748ff4

@ -3,6 +3,7 @@ package au.com.royalpay.payment.manage.management.clearing.core;
import au.com.royalpay.payment.manage.support.abafile.ABAFile;
import au.com.royalpay.payment.manage.tradelog.beans.ClearingLogQuery;
import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
@ -94,4 +95,6 @@ public interface CleanService {
void lockClearingLog(Date date, int clearingId);
void undoSettle(Date date, int clearingId);
ByteArrayResource downloadBatchSettleReportXlsx(int batchId);
}

@ -17,6 +17,9 @@ import au.com.royalpay.payment.manage.support.abafile.ABAConfig;
import au.com.royalpay.payment.manage.support.abafile.ABAFile;
import au.com.royalpay.payment.manage.support.abafile.ABATemplate;
import au.com.royalpay.payment.manage.support.abafile.SettleRemarkTemplateDescriber;
import au.com.royalpay.payment.manage.support.poi.ExcelFileBuilder;
import au.com.royalpay.payment.manage.support.poi.ExcelTemplate;
import au.com.royalpay.payment.manage.support.poi.FontFactory;
import au.com.royalpay.payment.manage.tradelog.beans.ClearingLogQuery;
import au.com.royalpay.payment.tools.connections.mpsupport.MpWechatApi;
import au.com.royalpay.payment.tools.connections.mpsupport.MpWechatApiProvider;
@ -59,6 +62,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.format.number.CurrencyStyleFormatter;
import org.springframework.stereotype.Service;
@ -1357,6 +1361,40 @@ public class CleanServiceImpl implements CleanService, ManagerTodoNoticeProvider
clearingLogMapper.deleteSettleLogs(clearingId);
}
@Override
public ByteArrayResource downloadBatchSettleReportXlsx(int batchId) {
List<JSONObject> mchList = clearingDetailMapper.batchReport(batchId);
if (mchList.isEmpty()) {
throw new NotFoundException("No batch found");
}
ExcelTemplate template = new ExcelTemplate()
.addSheet("Report")
.addStringColumn("client_moniker").setFontFactory(new FontFactory(true)).ok()
.addStringColumn("short_name").ok()
.addStringColumn("org_name").ok()
.addStringColumn("bd_user_name").ok()
.addStringColumn("city").ok()
.addStringColumn("clean_days").ok()
.addColumn(Cell.CELL_TYPE_NUMERIC, "clearing_amount", json -> json.getBigDecimal("clearing_amount").setScale(2, RoundingMode.DOWN).toPlainString()).setCellStyle(ExcelTemplate.STYLE_RIGHT).ok()
.addDateColumn("yyyy-MM-dd", "settle_date_from").ok()
.addDateColumn("yyyy-MM-dd", "settle_date_to").ok()
.build();
ExcelFileBuilder builder = new ExcelFileBuilder(template);
builder.bindSingleSheetData(mchList);
try (ByteArrayOutputStream bout = new ByteArrayOutputStream()) {
builder.build(bout);
bout.flush();
return new ByteArrayResource(bout.toByteArray()) {
@Override
public String getFilename() {
return "settle_report_" + DateFormatUtils.format(mchList.get(0).getDate("report_date"), "yyyyMMdd") + "_" + batchId + ".xlsx";
}
};
} catch (IOException e) {
throw new RuntimeException("Export excel failed", e);
}
}
private void releaseDistributedSurcharge(JSONObject clearingDetail) {
int clientId = clearingDetail.getIntValue("client_id");
BigDecimal distributedSurcharge = clearingDetail.getBigDecimal("distributed_surcharge");

@ -4,10 +4,12 @@ import au.com.royalpay.payment.manage.management.clearing.core.CleanService;
import au.com.royalpay.payment.manage.permission.manager.ManagerMapping;
import au.com.royalpay.payment.manage.permission.manager.RequireManager;
import au.com.royalpay.payment.manage.support.abafile.ABATemplate;
import au.com.royalpay.payment.tools.permission.enums.ManagerRole;
import au.com.royalpay.payment.tools.CommonConsts;
import au.com.royalpay.payment.tools.exceptions.BadRequestException;
import au.com.royalpay.payment.tools.permission.enums.ManagerRole;
import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -148,7 +150,7 @@ public class SettlementDevController {
return cleanService.findLogSettleByDate(dt);
}
@RequestMapping("/reports/{date}/settlement_aba")
@GetMapping("/reports/{date}/settlement_aba")
public void getSettlementABA(@PathVariable String date, HttpServletResponse resp) throws IOException {
try {
Date dt = dateFormat.parse(date);
@ -158,12 +160,17 @@ public class SettlementDevController {
}
}
@RequestMapping("/settle_batches/{batchId}/settle_files")
@GetMapping("/settle_batches/{batchId}/settle_files")
public void getSettlementFilesForBatch(@PathVariable String batchId, HttpServletResponse resp) throws IOException {
cleanService.getSettlementFilesForBatch(batchId, resp);
}
@RequestMapping("/details/{detailId}")
@GetMapping("/settle_batches/{batchId}/settle_report_xlsx")
public ByteArrayResource getSettlementReportForBatch(@PathVariable int batchId) {
return cleanService.downloadBatchSettleReportXlsx(batchId);
}
@GetMapping("/details/{detailId}")
public JSONObject settlementDetail(@PathVariable int detailId, @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) {
return cleanService.getCleanLogTransactions(detailId, manager);
}

@ -49,4 +49,6 @@ public interface ClearingDetailMapper {
@AutoSql(type = SqlType.DELETE)
void deleteSettleLogs(@Param("clearing_id") int clearingId);
List<JSONObject> batchReport(@Param("clearing_id") int clearingId);
}

@ -0,0 +1,56 @@
package au.com.royalpay.payment.manage.support.poi;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Create by davep at 2020-03-12 13:27
*/
public class CellStyleFactory {
private final short borderTop;
private final short borderBottom;
private final short borderLeft;
private final short borderRight;
private final short align;
private final short valign;
public CellStyleFactory(short borderTop, short borderBottom, short borderLeft, short borderRight, short align, short valign) {
this.borderTop = borderTop;
this.borderBottom = borderBottom;
this.borderLeft = borderLeft;
this.borderRight = borderRight;
this.align = align;
this.valign = valign;
}
public CellStyleFactory(short borderTopBottom, short borderLeftRight, short align, short valign) {
this(borderTopBottom, borderTopBottom, borderLeftRight, borderLeftRight, align, valign);
}
public CellStyleFactory(short border, short align, short valign) {
this(border, border, align, valign);
}
public CellStyleFactory(short border, short align) {
this(border, border, align, CellStyle.VERTICAL_CENTER);
}
public CellStyle buildCellStyle(Workbook workbook, Font font) {
CellStyle style = workbook.createCellStyle();
style.setBorderBottom(borderBottom);
style.setBorderRight(borderRight);
style.setBorderLeft(borderLeft);
style.setBorderTop(borderTop);
style.setAlignment(align);
style.setVerticalAlignment(valign);
if (font != null) {
style.setFont(font);
}
return style;
}
public CellStyle buildCellStyle(Workbook workbook) {
return buildCellStyle(workbook, null);
}
}

@ -0,0 +1,72 @@
package au.com.royalpay.payment.manage.support.poi;
import com.alibaba.fastjson.JSONObject;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Create by davep at 2020-03-12 11:59
*/
public class ExcelFileBuilder {
private ExcelTemplate template;
private Map<String, List<JSONObject>> sheetsData;
private boolean blocked = false;
public ExcelFileBuilder(ExcelTemplate template) {
this.template = template;
this.sheetsData = new HashMap<>();
}
public ExcelFileBuilder bindData(String sheetName, List<JSONObject> data) {
checkBlock();
if (template.hasSheet(sheetName)) {
sheetsData.put(sheetName, data);
} else {
throw new IllegalArgumentException("Sheet name '" + sheetName + "' not exists");
}
return this;
}
public void checkBlock() {
if (blocked) {
throw new IllegalStateException("Builder blocked");
}
}
public ExcelFileBuilder bindSingleSheetData(List<JSONObject> data) {
if (template.getSheets().size() == 1) {
String name = new ArrayList<>(template.getSheets().keySet()).get(0);
bindData(name, data);
} else {
throw new IllegalArgumentException("There are more than one sheet");
}
return this;
}
public void build(OutputStream out) {
blocked = true;
try (Workbook workbook = new XSSFWorkbook()) {
Map<String, ExcelTemplate.SheetTemplate> sheetTpls = template.getSheets();
for (Map.Entry<String, ExcelTemplate.SheetTemplate> sheetTpl : sheetTpls.entrySet()) {
Sheet sheet = workbook.createSheet(sheetTpl.getKey());
List<JSONObject> data = sheetsData.get(sheetTpl.getKey());
if (data == null) {
continue;
}
sheetTpl.getValue().buildSheet(sheet, data);
}
workbook.write(out);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,186 @@
package au.com.royalpay.payment.manage.support.poi;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.ss.usermodel.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Create by davep at 2020-03-12 11:59
*/
public class ExcelTemplate {
public static final CellStyleFactory STYLE_LEFT = new CellStyleFactory(CellStyle.BORDER_MEDIUM, CellStyle.ALIGN_LEFT);
public static final CellStyleFactory STYLE_RIGHT = new CellStyleFactory(CellStyle.BORDER_MEDIUM, CellStyle.ALIGN_RIGHT);
private Map<String, SheetTemplate> sheets;
public ExcelTemplate() {
this.sheets = new LinkedHashMap<>();
}
public SheetTemplate addSheet(String name) {
SheetTemplate sheetTpl = new SheetTemplate(this, name);
sheets.put(name, sheetTpl);
return sheetTpl;
}
protected Map<String, SheetTemplate> getSheets() {
return sheets;
}
protected boolean hasSheet(String sheetName) {
return sheets.containsKey(sheetName);
}
public static class SheetTemplate {
private ExcelTemplate parent;
private String name;
private List<ColumnTemplate> columns;
private SheetTemplate(ExcelTemplate parent, String name) {
this.parent = parent;
this.name = name;
this.columns = new ArrayList<>();
}
public ExcelTemplate build() {
return parent;
}
public SheetTemplate newSheet(String name) {
return parent.addSheet(name);
}
public ColumnTemplate addColumn(int columnType, String name, ValueProvider<?> provider) {
ColumnTemplate col = new ColumnTemplate(this, name, columnType, provider);
columns.add(col);
return col;
}
public ColumnTemplate addDateColumn(String pattern, String name) {
return addDateColumn(pattern, name, name);
}
public ColumnTemplate addDateColumn(String pattern, String name, String key) {
return this.addColumn(Cell.CELL_TYPE_STRING, name, json -> DateFormatUtils.format(json.getDate(key), pattern));
}
public ColumnTemplate addNumberColumn(String name) {
return this.addNumberColumn(name, name);
}
public ColumnTemplate addNumberColumn(String name, String key) {
return this.addColumn(Cell.CELL_TYPE_NUMERIC, name, json -> json.getString(key));
}
public ColumnTemplate addStringColumn(String name) {
return this.addStringColumn(name, name);
}
public ColumnTemplate addStringColumn(String name, String key) {
return this.addColumn(Cell.CELL_TYPE_STRING, name, json -> json.getString(key));
}
public ColumnTemplate addAutoTypeColumn(String name, ValueProvider<?> provider) {
ColumnTemplate col = new ColumnTemplate(this, name, null, provider);
columns.add(col);
return col;
}
protected String getName() {
return name;
}
protected List<ColumnTemplate> getColumns() {
return columns;
}
public void buildSheet(Sheet sheet, List<JSONObject> data) {
int rowNum = 0;
Row titleRow = sheet.createRow(rowNum);
rowNum++;
Workbook workbook = sheet.getWorkbook();
Font boldFont = PoiUtils.initBoldFont(workbook);
CellStyle titleStyle = new CellStyleFactory(CellStyle.BORDER_MEDIUM, CellStyle.ALIGN_CENTER).buildCellStyle(workbook, boldFont);
for (int i = 0, len = columns.size(); i < len; i++) {
ColumnTemplate col = columns.get(i);
Cell cell = titleRow.createCell(i, Cell.CELL_TYPE_STRING);
cell.setCellStyle(titleStyle);
cell.setCellValue(col.name);
}
for (JSONObject item : data) {
Row dataRow = sheet.createRow(rowNum);
rowNum++;
for (int i = 0, len = columns.size(); i < len; i++) {
ColumnTemplate col = columns.get(i);
col.buildCell(dataRow, i, item);
}
}
}
}
public static class ColumnTemplate {
private SheetTemplate parent;
private String name;
private ValueProvider<?> valueProvider;
private Integer columnType;
private CellStyleFactory cellStyle;
private FontFactory fontFactory;
public ColumnTemplate(SheetTemplate parent, String name, Integer columnType, ValueProvider<?> valueProvider) {
this.parent = parent;
this.name = name;
this.columnType = columnType;
this.valueProvider = valueProvider;
}
public ColumnTemplate setCellStyle(CellStyleFactory cellStyle) {
this.cellStyle = cellStyle;
return this;
}
public SheetTemplate ok() {
return parent;
}
protected void buildCell(Row row, int columnNum, JSONObject data) {
Object value = valueProvider.getValue(data);
if (value == null) {
row.createCell(columnNum, Cell.CELL_TYPE_BLANK);
return;
}
if (columnType == null) {
columnType = PoiUtils.convertCellType(value.getClass());
}
CellStyle style = initCellStyle(row.getSheet().getWorkbook(), columnType);
Cell cell = row.createCell(columnNum, columnType);
cell.setCellStyle(style);
PoiUtils.setCellValue(cell, value);
}
private CellStyle initCellStyle(Workbook workbook, Integer columnType) {
Font font = fontFactory != null ? fontFactory.buildFont(workbook) : null;
if (cellStyle == null) {
if (columnType == Cell.CELL_TYPE_NUMERIC || this.columnType == Cell.CELL_TYPE_BOOLEAN) {
return STYLE_RIGHT.buildCellStyle(workbook, font);
} else {
return STYLE_LEFT.buildCellStyle(workbook, font);
}
} else {
return cellStyle.buildCellStyle(workbook, font);
}
}
public ColumnTemplate setFontFactory(FontFactory fontFactory) {
this.fontFactory = fontFactory;
return this;
}
}
public interface ValueProvider<T> {
T getValue(JSONObject item);
}
}

@ -0,0 +1,36 @@
package au.com.royalpay.payment.manage.support.poi;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Workbook;
/**
* Create by davep at 2020-03-12 13:27
*/
public class FontFactory {
private boolean bold;
private short color;
private boolean italic;
public FontFactory(boolean bold, short color, boolean italic) {
this.bold = bold;
this.color = color;
this.italic = italic;
}
public FontFactory(boolean bold, boolean italic) {
this(bold, Font.COLOR_NORMAL, italic);
}
public FontFactory(boolean bold) {
this(bold, false);
}
public Font buildFont(Workbook workbook) {
Font font = workbook.createFont();
font.setBold(bold);
font.setColor(color);
font.setItalic(italic);
return font;
}
}

@ -0,0 +1,75 @@
package au.com.royalpay.payment.manage.support.poi;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Workbook;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Create by davep at 2020-03-12 12:43
*/
public class PoiUtils {
private static final Map<Class<?>, Class<?>> PRIMITIVE_CLASSES = new HashMap<>();
static {
PRIMITIVE_CLASSES.put(boolean.class, Boolean.class);
PRIMITIVE_CLASSES.put(byte.class, Byte.class);
PRIMITIVE_CLASSES.put(short.class, Short.class);
PRIMITIVE_CLASSES.put(char.class, Character.class);
PRIMITIVE_CLASSES.put(int.class, Integer.class);
PRIMITIVE_CLASSES.put(long.class, Long.class);
PRIMITIVE_CLASSES.put(float.class, Float.class);
PRIMITIVE_CLASSES.put(double.class, Double.class);
PRIMITIVE_CLASSES.put(void.class, Void.class);
}
public static Font initBoldFont(Workbook workbook) {
Font font = workbook.createFont();
font.setBold(true);
return font;
}
public static int convertCellType(Class<?> valueClass) {
if (valueClass.isPrimitive()) {
valueClass = PRIMITIVE_CLASSES.get(valueClass);
}
if (Number.class.isAssignableFrom(valueClass)) {
return Cell.CELL_TYPE_NUMERIC;
}
if (Date.class.isAssignableFrom(valueClass) || Temporal.class.isAssignableFrom(valueClass)) {
return Cell.CELL_TYPE_NUMERIC;
}
if (Boolean.class.isAssignableFrom(valueClass)) {
return Cell.CELL_TYPE_BOOLEAN;
}
return Cell.CELL_TYPE_STRING;
}
public static void setCellValue(Cell cell, Object value) {
Class valueClass = value.getClass();
if (valueClass.isPrimitive()) {
valueClass = PRIMITIVE_CLASSES.get(valueClass);
}
if (Number.class.isAssignableFrom(valueClass)) {
cell.setCellValue((double) value);
return;
}
if (Date.class.isAssignableFrom(valueClass)) {
cell.setCellValue(((Date) value).getTime() / 1000);
return;
}
if (Boolean.class.isAssignableFrom(valueClass)) {
cell.setCellValue((boolean) value);
return;
}
cell.setCellValue(value.toString());
}
}

@ -86,4 +86,35 @@
<if test="end!=null">and report_date &lt;= #{end}</if>
</where>
</select>
<select id="batchReport" resultType="com.alibaba.fastjson.JSONObject">
<![CDATA[
SELECT c.client_moniker,
c.short_name,
o.name org_name,
c.bd_user_name,
(
SELECT b.city
FROM financial_bd_config b
INNER JOIN sys_client_bd cb ON cb.bd_id = b.manager_id
WHERE cb.client_id = c.client_id
AND cb.start_date <= d.report_date
AND (
cb.end_date IS NULL
OR cb.end_date >= d.report_date
)
ORDER BY cb.create_time DESC
LIMIT 1
) city,
concat('T+', d.clear_days) clean_days,
d.clearing_amount,
d.settle_date_from,
d.settle_date_to,
d.report_date
FROM log_clearing_detail d
INNER JOIN sys_clients c ON c.client_id = d.client_id
inner join sys_org o on o.org_id = c.org_id
WHERE d.clearing_id = #{clearing_id}
ORDER BY clearing_amount DESC
]]>
</select>
</mapper>

@ -503,6 +503,14 @@ define(['angular', 'decimal', 'uiBootstrap', 'uiRouter', 'angularEcharts'], func
};
return info
});
$scope.sumSelectedAmount = function(){
let sendingLogs = $scope.settleLogs.filter(log=>log.send);
if(sendingLogs.length){
return sendingLogs.map(log=>log.amount).reduce((m1,m2)=>m1+m2);
}else{
return $scope.settleLogs.map(log=>log.amount).reduce((m1,m2)=>m1+m2);
}
};
$scope.config = {mark_sent: true};
$scope.switchSendFlag=function(info){
info.send=!info.send

@ -159,6 +159,9 @@
<a class="btn btn-primary" ng-href="/sys/settlement/settle_batches/{{analysisFilter.clearing_id}}/settle_files" target="_blank">
Download Files
</a>
<a class="btn btn-primary" ng-href="/sys/settlement/settle_batches/{{analysisFilter.clearing_id}}/settle_report_xlsx" target="_blank">
Download Settle Report
</a>
</div>
</div>

@ -2,36 +2,43 @@
<h4>Send Settlement Email</h4>
</div>
<div class="modal-body" style="height: 300px">
<div class="text-center" >
<div class="text-center">
<div class="row">
<div class="col-sm-12">
<ul class="list-group">
<li class="list-group-item text-left" ng-repeat="log in settleLogs" ng-class="{'active':log.send}" ng-click="switchSendFlag(log)">
<span>[{{log.remark}}]{{log.id}}</span>
<span class="pull-right" ng-bind="log.amount|currency:''"></span>
</li>
</ul>
</div>
<div class="col-sm-12">Select none same as select every batch</div>
<div class="col-sm-12">
<ul class="list-group">
<li class="list-group-item text-left" ng-repeat="log in settleLogs" ng-class="{'active':log.send}"
ng-click="switchSendFlag(log)">
<span>[{{log.remark}}]{{log.id}}</span>
<span class="pull-right" ng-bind="log.amount|currency:''"></span>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<label class="checkbox-inline">
<input type="checkbox" name="mark_sent" ng-model="config.mark_sent" class="checkbox">
同时标记为已发送
</label>
</div>
<div class="col-sm-12">
Total Settle Amount: {{sumSelectedAmount()|currency:''}}
</div>
</div>
<div class="row">
<div class="col-sm-12">
<button type="button" class="btn btn-primary" style="width: 100%; margin-top: 30px" data-dismiss="modal"
ng-click="sendSettlementMail()" ng-disabled="sendMailButton">发送清算邮件
</button>
</div>
<div class="col-sm-12">
<label class="checkbox-inline">
<input type="checkbox" name="mark_sent" ng-model="config.mark_sent" class="checkbox">
Mark batch as sent same time.
</label>
</div>
</div>
<div class="row">
<div class="error">
<span ng-bind="error_msg"></span>
<div class="col-sm-12">
<button type="button" class="btn btn-primary" style="width: 100%; margin-top: 30px" data-dismiss="modal"
ng-click="sendSettlementMail()" ng-disabled="sendMailButton">Send Settlement Mail
</button>
</div>
</div>
<div class="row">
<div class="error">
<span ng-bind="error_msg"></span>
</div>
</div>
</div>
</div>
Loading…
Cancel
Save