Merge branch 'develop'

master
yixian 4 years ago
commit 63c59702a7

@ -1,11 +1,16 @@
package au.com.royalpay.payment.manage.management.clearing.core; package au.com.royalpay.payment.manage.management.clearing.core;
import au.com.royalpay.payment.manage.management.clearing.web.ManualSettleTask;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import java.util.List; import java.util.List;
public interface SettleTasksService { public interface SettleTasksService {
List<JSONObject> listLiveTasks(); List<JSONObject> listPlans();
JSONObject currentProcessingStatus(); JSONObject currentProcessingStatus();
List<JSONObject> listMerchantsInfo(String mchMonikers);
void submitTasks(List<ManualSettleTask> tasks);
} }

@ -1,31 +1,55 @@
package au.com.royalpay.payment.manage.management.clearing.core.impl; package au.com.royalpay.payment.manage.management.clearing.core.impl;
import au.com.royalpay.payment.manage.management.clearing.core.SettleTasksService; import au.com.royalpay.payment.manage.management.clearing.core.SettleTasksService;
import au.com.royalpay.payment.manage.management.clearing.web.ManualSettleTask;
import au.com.royalpay.payment.manage.mappers.system.SysSettlePlanMapper; import au.com.royalpay.payment.manage.mappers.system.SysSettlePlanMapper;
import au.com.royalpay.payment.tools.exceptions.ServerErrorException;
import au.com.royalpay.payment.tools.merchants.core.MerchantInfoProvider;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Service @Service
public class SettleTaskServiceImpl implements SettleTasksService { public class SettleTaskServiceImpl implements SettleTasksService {
private SysSettlePlanMapper planMapper; private final SysSettlePlanMapper planMapper;
private StringRedisTemplate redisTemplate; private final StringRedisTemplate redisTemplate;
private String prefix; private final MerchantInfoProvider mchInfoProvider;
private final String prefix;
private final RestTemplate restTemplate;
private final String transactionHost;
private static final String SETTLE_TASK_URI = "/dev/manual/settle_tasks";
public SettleTaskServiceImpl(SysSettlePlanMapper planMapper, StringRedisTemplate redisTemplate, public SettleTaskServiceImpl(SysSettlePlanMapper planMapper, StringRedisTemplate redisTemplate,
@Value("${app.redis.prefix}") String prefix) { MerchantInfoProvider mchInfoProvider, @Value("${app.redis.prefix}") String prefix,
RestTemplate restTemplate, @Value("${platform.services.transaction:http://127.0.0.1:6012}") String transactionHost) {
this.planMapper = planMapper; this.planMapper = planMapper;
this.redisTemplate = redisTemplate; this.redisTemplate = redisTemplate;
this.mchInfoProvider = mchInfoProvider;
this.prefix = prefix; this.prefix = prefix;
this.restTemplate = restTemplate;
this.transactionHost = transactionHost;
} }
@Override @Override
public List<JSONObject> listLiveTasks() { public List<JSONObject> listPlans() {
return planMapper.listPlans(); return planMapper.listPlans();
} }
@ -48,4 +72,62 @@ public class SettleTaskServiceImpl implements SettleTasksService {
} }
return stdObj; return stdObj;
} }
@Override
public List<JSONObject> listMerchantsInfo(String mchMonikers) {
List<String> monikers = new ArrayList<>(Arrays.asList(formatStringArray(mchMonikers).split(",")));
return monikers.parallelStream().map(this::getSimplifiedClientsInfo).filter(Objects::nonNull).collect(Collectors.toList());
}
private JSONObject getSimplifiedClientsInfo(String moniker) {
JSONObject info = mchInfoProvider.getClientInfoByMoniker(moniker);
if (info == null) {
return null;
}
JSONObject simple = new JSONObject();
simple.put("client_moniker", info.getString("client_moniker"));
simple.put("clean_days", info.getIntValue("clean_days"));
simple.put("bd_user_name", info.getString("bd_user_name"));
return simple;
}
@Override
public void submitTasks(List<ManualSettleTask> tasks) {
URI uri = UriComponentsBuilder.fromHttpUrl(transactionHost).replacePath(SETTLE_TASK_URI)
.build().toUri();
try {
restTemplate.exchange(RequestEntity.post(uri).contentType(MediaType.APPLICATION_JSON)
.body(JSON.toJSONString(tasks).getBytes(StandardCharsets.UTF_8)), String.class);
} catch (RestClientResponseException e) {
String resp = e.getResponseBodyAsString();
try {
throw new ServerErrorException("Transaction response error:" + JSON.parseObject(resp).getString("message"));
} catch (JSONException e1) {
throw new ServerErrorException("Connect Transaction error:" + resp);
}
} catch (RestClientException e) {
throw new ServerErrorException("Connect Transaction error:" + e.getMessage());
}
}
public static String formatStringArray(String str) {
str = str.replace("", ",");
str = str.replace(" ", ",");
str = str.replace("\r\n", ",");
str = str.replace("\n", ",");
str = str.replace("\r", ",");
String nStr = str.replace(",,", ",");
while (!nStr.equals(str)) {
str = nStr;
nStr = str.replace(",,", ",");
}
str = nStr;
if (str.endsWith(",")) {
str = StringUtils.substring(str, 0, -1);
}
if (str.startsWith(",")) {
str = StringUtils.substring(str, 1);
}
return str;
}
} }

@ -0,0 +1,91 @@
package au.com.royalpay.payment.manage.management.clearing.web;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.annotation.JSONType;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import java.util.Date;
import java.util.List;
@JSONType(naming = PropertyNamingStrategy.SnakeCase)
public class ManualSettleTask {
private String taskId;
private String settleDate;
private String planRefId;
private String remark;
private String lastSettleDate;
private int[] disabledCleanDays;
private List<String> whitelistClients;
private boolean followSubPartner;
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public Date getSettleDateValue() {
return StringUtils.isEmpty(settleDate) ? DateTime.now().withMillisOfDay(0).toDate() : DateTime.parse(settleDate).toDate();
}
public String getSettleDate() {
return settleDate;
}
public void setSettleDate(String settleDate) {
this.settleDate = settleDate;
}
public String getPlanRefId() {
return planRefId;
}
public void setPlanRefId(String planRefId) {
this.planRefId = planRefId;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getLastSettleDate() {
return lastSettleDate;
}
public void setLastSettleDate(String lastSettleDate) {
this.lastSettleDate = lastSettleDate;
}
public int[] getDisabledCleanDays() {
return disabledCleanDays;
}
public void setDisabledCleanDays(int[] disabledCleanDays) {
this.disabledCleanDays = disabledCleanDays;
}
public List<String> getWhitelistClients() {
return whitelistClients;
}
public void setWhitelistClients(List<String> whitelistClients) {
this.whitelistClients = whitelistClients;
}
public boolean isFollowSubPartner() {
return followSubPartner;
}
public void setFollowSubPartner(boolean followSubPartner) {
this.followSubPartner = followSubPartner;
}
}

@ -5,6 +5,8 @@ import au.com.royalpay.payment.manage.permission.manager.ManagerMapping;
import au.com.royalpay.payment.tools.permission.enums.ManagerRole; import au.com.royalpay.payment.tools.permission.enums.ManagerRole;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
@ -18,13 +20,24 @@ public class SettleTasksController {
this.svc = svc; this.svc = svc;
} }
@GetMapping @GetMapping("/plans")
public List<JSONObject> listTasks() { public List<JSONObject> listPlans() {
return svc.listLiveTasks(); return svc.listPlans();
} }
@GetMapping("/current_progress") @GetMapping("/current_progress")
public JSONObject getProcessingStatus() { public JSONObject getProcessingStatus() {
return svc.currentProcessingStatus(); return svc.currentProcessingStatus();
} }
@PostMapping("/merchants")
public List<JSONObject> listMerchants(@RequestBody JSONObject mch) {
return svc.listMerchantsInfo(mch.getString("monikers"));
}
@PostMapping
public JSONObject submitTasks(@RequestBody List<ManualSettleTask> tasks) {
svc.submitTasks(tasks);
return getProcessingStatus();
}
} }

@ -0,0 +1,212 @@
define(['angular', 'uiBootstrap', 'uiRouter'], function (angular) {
'use strict';
let app = angular.module('settleTasks', ['ui.bootstrap', 'ui.router']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider.state('settle_tasks', {
url: '/settle_tasks',
templateUrl: '/static/analysis/templates/settle_tasks_index.html',
controller: 'settleTasksRootCtrl'
})
}]);
app.controller('settleTasksRootCtrl', ['$scope', '$http', '$interval', '$q', '$uibModal', 'commonDialog',
function ($scope, $http, $interval, $q, $uibModal, commonDialog) {
let ctrl = this;
$scope.loadPlans = function () {
$http.get('/sys/settle_tasks/plans').then(function (res) {
$scope.staticPlans = res.data;
for (let plan of $scope.staticPlans) {
plan.name = plan.remark || plan.plan_id;
}
})
};
$scope.checkProgressStatus = function () {
let defer = $q.defer();
$http.get('/sys/settle_tasks/curent_progress').then(function (res) {
$scope.progress = res.data;
defer.resolve();
if (!$scope.progress.processing) {
if (ctrl.update_task) {
$interval.cancel(ctrl.update_task);
}
if (!$scope.currentTasks) {
$scope.currentTasks = [];
}
}
let finished = $scope.progress.finished_sequence;
if ($scope.currentTasks) {
for (let finId of finished) {
$scope.currentTasks.filter(task => task.task_id === finId).forEach(v => {
v.idle = false;
v.processed = true;
v.processint = false;
})
}
if ($scope.progress.processing) {
$scope.currentTasks.filter(task => task.task_id === $scope.progress.progress_id).forEach(v => {
v.idle = false;
v.processed = false;
v.processing = true;
})
}
}
})
return defer.promise;
};
$scope.loadPlans();
$scope.checkProgressStatus();
$scope.openBatchAddMerchantsDialog = function () {
if (!$scope.editingTask) {
return;
}
$uibModal.open({
templateUrl: '/static/analysis/templates/settle_tasks_dialog_batch_add_merchants.html',
controller: 'batchAddMerchantsDialogCtrl',
size: 'lg'
}).result.then(function (merchants) {
let arr = $scope.editingTask.whitelist_clients;
for (let mch of merchants) {
if (arr.filter(wMch => wMch.client_moniker === mch.client_moniker).length < 1) {
arr.push(mch);
}
}
$scope.editingTask.whitelist_clients = arr;
})
};
$scope.removeFromTask = function (mch) {
let idx = $scope.editingTask.whitelist_clients.indexOf(mch);
if (idx >= 0) {
$scope.editingTask.whitelist_clients.splice(idx, 1);
}
};
$scope.newTask = function () {
$uibModal.open({
size: 'lg',
templateUrl: '/static/analysis/templates/settle_tasks_new_task_dialog.html',
controller: 'newSettleTaskDialogCtrl',
resolve: {
plans: function () {
return $scope.staticPlans;
}
}
}).result.then(function (task) {
task.idle = true;
task.processing = false;
task.processed = false;
$scope.editingTask = task;
$scope.currentTasks.push(task);
})
};
$scope.submitTasks = function () {
let tasks = [];
for (let task of $scope.currentTasks) {
let submitTask = {
task_id: task.task_id,
settle_date: task.settle_date,
remark: task.remark,
};
if (task.plan) {
submitTask.plan_ref_id = task.plan.plan_id;
} else {
submitTask.last_settle_date = task.last_settle_date;
submitTask.disabled_clean_days = task.disabled_clean_days;
submitTask.follow_sub_partner = task.follow_sub_partner;
submitTask.whitelist_clients = task.whitelist_clients.map(cli => cli.client_moniker)
}
tasks.push(submitTask)
}
$http.post('/sys/settle_tasks', tasks).then(function () {
commonDialog.alert({type: 'success', title: 'Success', content: resp.data.message})
ctrl.update_task = $interval(function () {
$scope.checkProgressStatus()
}, 5000);
}, function (resp) {
commonDialog.alert({type: 'error', title: 'Error', content: resp.data.message})
})
}
}]);
app.controller('batchAddMerchantsDialogCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.data = {client_monikers: ''};
$scope.sync = false;
$scope.analysisMerchants = function () {
$scope.sync = true;
$http.post('/sys/settle_tasks/merchants', {monikers: $scope.data.client_monikers}).then(function (res) {
$scope.merchantsInfo = res.data;
$scope.sync = false;
}, function () {
$scope.sync = false;
})
};
$scope.submit = function () {
$scope.$close($scope.merchantsInfo)
}
}]);
app.controller('newSettleTaskDialogCtrl', ['$scope', '$filter', 'plans', function ($scope, $filter, plans) {
$scope.plans = plans;
$scope.task = {
t1: true,
t2: true,
t3: true,
remark: ''
};
$scope.lastTransDate = {
open: false
}
$scope.settleDate = {
open: false
}
$scope.today = new Date();
$scope.submitTask = function () {
let chosenPlan = null;
if (!$scope.task.remark) {
$scope.task.remark = $filter('date')($scope.task.settle_date, 'yyyy-MM-dd') + ' Settle'
}
if ($scope.task.plan) {
let arr = $scope.plans.filter(p => p.plan_id === $scope.task.plan)
chosenPlan = arr.length ? arr[0] : null;
$scope.$close({
plan_id: $scope.plan.remark.replace(' ', '') + randomString(6),
settle_date: $filter('date')($scope.task.settle_date, 'yyyy-MM-dd'),
remark: $scope.task.remark,
plan: chosenPlan
})
} else {
let cleanDays = [];
if (!$scope.task.t1) {
cleanDays.push(1);
}
if (!$scope.task.t2) {
cleanDays.push(2)
}
if (!$scope.task.t3) {
cleanDays.push(3);
}
$scope.$close({
plan_id: $scope.plan.remark.replace(' ', '') + randomString(6),
disabled_clean_days: cleanDays,
settle_date: $filter('date')($scope.task.settle_date, 'yyyy-MM-dd'),
last_settle_date: $scope.task.last_settle_date != null ? $filter('date')($scope.task.last_settle_date, 'yyyy-MM-dd') : null,
remark: $scope.task.remark,
follow_sub_partner: $scope.plan.follow_sub_partner
})
}
};
function randomString(length) {
const source = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
let randomStr = '';
for (let i = 0; i < length; i++) {
randomStr += source.charAt(Math.floor(Math.random() * source.length))
}
return randomStr;
}
}])
})

@ -1,8 +0,0 @@
define(['angular', 'uiBootstrap', 'uiRouter'], function (angular) {
'use strict';
let app = angular.module('settleTools', ['ui.bootstrap', 'ui.router']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider.state('')
}])
})

@ -0,0 +1,11 @@
<div class="modal-header">Batch Add Merchants</div>
<div class="modal-body">
<p>Paste client monikers below</p>
<p>Merchants Count: {{$scope.merchantsInfo.length}}</p>
<textarea placeholder="Client Monikers" rows="10" ng-change="analysisMerchants()"
ng-model="data.client_monikers"></textarea>
</div>
<div class="modal-footer">
<div class="btn btn-success" ng-click="submit()" ng-disabled="sync">OK</div>
<div class="btn btn-danger" ng-click="$dismiss()">Cancel</div>
</div>

@ -0,0 +1,115 @@
<div>
<section class="content-header">
<h1>Settle Tasks</h1>
<ol class="breadcrumb">
<li>
<i class="fa fa-list-alt"></i> Analysis
</li>
<li class="active">Settle Tasks</li>
</ol>
</section>
<section class="content">
<div class="box" ng-class="{'box-warning':progress.processing,'box-success':!progress.processing}">
<div class="box-header" ng-class="{'text-warning':progress.processing,'text-success':!progress.processing}">
Status: {{progress.processing?'Processing':'Idle'}}
</div>
<div class="box-body" ng-if="progress.processing">
<h4>{{progress.plan_remark}}|{{progress.current_step}}</h4>
<uib-progressbar value="progress.progress" animate="true" max-value="100"></uib-progressbar>
</div>
</div>
<div class="box box-info">
<div class="box-header">
Tasks Prepare
<button class="btn btn-success pull-right" ng-click="newTask()">New Task</button>
<button class="btn btn-warning pull-right" ng-click="submitTasks()">Execute Tasks</button>
</div>
<div class="box-body">
<div class="box" ng-repeat="task in currentTasks"
ng-class="{'box-success':task.processed,'box-default':task.idle,'box-warning':task.processing}">
<div class="box-header">
{{task.remark}}-{{task.settle_date}}
<div class="btn-group pull-right" ng-if="!progress.processing">
<button class="btn btn-primary" type="button" title="Hide/Show Detail"
ng-click="task.display_detail=!task.display_detail"><i class="fa fa-search"></i>
</button>
<button class="btn btn-primary" ng-if="!task.plan" type="button" title="Edit"
ng-click="editingTask=task"><i class="fa fa-edit"></i></button>
<button class="btn btn-danger" type="button" title="Drop" ng-click="dropTask(task)"><i
class="fa fa-trash"></i></button>
</div>
</div>
<div class="box-body" ng-show="task.display_detail">
<div ng-if="task.plan" ng-bind="task.plan.remark"></div>
<ul class="list-group" ng-if="!task.plan">
<li class="list-group-item">Last Settle Date:{{task.last_settle_date||'Unset'}}</li>
<li class="list-group-item">Disabled Clean Days:
<span ng-repeat="day in task.disabled_clean_days" ng-bind="'T+'+day+','"></span>
</li>
<li class="list-group-item">With Sub partners: {{task.follow_sub_partner?'Yes':'No'}}</li>
</ul>
<table class="table table-bordered table-striped table-hover"
ng-if="task.whitelist_clients.length">
<thead>
<tr>
<th>Client Moniker</th>
<th>BD Name</th>
<th>Clean Days</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cli in task.whitelist_clients">
<td ng-bind="cli.client_moniker"></td>
<td ng-bind="cli.bd_user_name"></td>
<td ng-bind="cli.clean_days"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="box box-info" ng-if="!progress.processing && editingTask!=null">
<div class="box-header">Editing Task</div>
<div class="box-body">
<div class="form-group">
<label class="control-label">Settle Date</label>
<div class="form-control-static" ng-bind="editingTask.settle_date"></div>
</div>
<button ng-disabled="processing" type="button" class="btn btn-success"
ng-click="openBatchAddMerchantsDialog()">Add Merchants
</button>
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Client Moniker</th>
<th>BD Name</th>
<th>Clean Days</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="cli in editingTask.whitelist_clients">
<td ng-bind="cli.client_moniker"></td>
<td ng-bind="cli.bd_user_name"></td>
<td ng-bind="cli.clean_days"></td>
<td>
<button class="btn btn-link text-danger" title="Del"
ng-click="removeFromTask(cli)">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
<tr ng-if="!editingTask.whitelist_clients.length">
<td colspan="4" class="text-center">No Merchants applied</td>
</tr>
</tbody>
</table>
</div>
<div class="box-box-footer">
<button class="btn btn-danger" ng-click="editingTask = null">Cancel</button>
</div>
</div>
</section>
</div>

@ -0,0 +1,66 @@
<div class="modal-header">New Task</div>
<div class="modal-body">
<form class="form-horizontal" name="newSettleTaskForm">
<div class="form-group">
<label class="control-label col-xs-4" for="taskRemark">Title</label>
<div class="col-xs-6">
<input class="form-control" required id="taskRemark" type="text" ng-model="task.remark">
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4" for="planSelect">Plan</label>
<div class="col-xs-6">
<select id="planSelect" ng-model="task.plan" ng-options="plan.plan_id as plan.name for plan in plans">
<option value="">Manual Add</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-4" for="settleDate">As Settle Date</label>
<div class="col-xs-6">
<input class="form-control" required id="settleDate"
ng-model="task.settle_date"
uib-datepicker-popup size="10" placeholder="Settle Date"
is-open="settleDate.open" ng-click="settleDate.open=true"
datepicker-options="{maxDate:today}">
</div>
</div>
<div class="form-group" ng-if="!task.plan">
<label class="control-label col-xs-4" for="lastSettleDate">Last Transaction Date(Inclusive)</label>
<div class="col-xs-6">
<input class="form-control" id="lastSettleDate"
ng-model="task.last_settle_date"
uib-datepicker-popup size="10" placeholder="Last Transaction Date(Inclusive)"
is-open="lastTransDate.open" ng-click="lastTransDate.open=true"
datepicker-options="{maxDate:today}">
</div>
</div>
<div class="form-group" ng-if="!task.plan">
<label class="control-label col-xs-4">Enable Clean Days</label>
<div class="col-xs-6 checkbox-inline">
<label>
<input type="checkbox" ng-model="task.t1">T+1
</label>
<label>
<input type="checkbox" ng-model="task.t2">T+2
</label>
<label>
<input type="checkbox" ng-model="task.t3">T+3
</label>
</div>
</div>
<div class="form-group" ng-if="!task.plan">
<label class="control-label col-xs-4" for="followSubPartnerCheck">Follow Sub Partner</label>
<div class="col-xs-6 checkbox-inline">
<input type="checkbox" ng-model="task.follow_sub_partner" id="followSubPartnerCheck">
</div>
</div>
<div class="button-group">
<button type="button" class="btn btn-success" ng-disabled="newSettleTaskForm.$invalid"
ng-click="submitTask()">OK
</button>
<button type="button" class="btn btn-danger" ng-click="$dismiss()">Cancel</button>
</div>
</form>
</div>

@ -186,8 +186,8 @@
<div class="form-inline"> <div class="form-inline">
<div class="input-group"> <div class="input-group">
<input class="form-control" ng-model="search.client_moniker"> <input class="form-control" ng-model="search.client_moniker">
<div class="input-group-addon"> <div class="input-group-btn">
<button class="input-group-btn" type="button" ng-click="searchClient()"><i <button class="btn btn-primary" type="button" ng-click="searchClient()"><i
class="fa fa-search"></i></button> class="fa fa-search"></i></button>
</div> </div>
</div> </div>

Loading…
Cancel
Save