风控模块

master
taylor.dang 6 years ago
parent 4594faee58
commit 87e956caba

@ -13,6 +13,10 @@ import au.com.royalpay.payment.manage.bill.core.BillOrderService;
import au.com.royalpay.payment.manage.bill.core.BillService;
import au.com.royalpay.payment.manage.merchants.beans.ClientAuthFilesInfo;
import au.com.royalpay.payment.manage.merchants.beans.ClientUpdateInfo;
import au.com.royalpay.payment.manage.riskbusiness.bean.RiskEventQuery;
import au.com.royalpay.payment.manage.riskbusiness.core.RiskBusinessService;
import au.com.royalpay.payment.manage.riskbusiness.core.RiskUploadService;
import au.com.royalpay.payment.manage.riskbusiness.enums.RiskResultTypeEnum;
import au.com.royalpay.payment.manage.settlement.core.ManualSettleSupport;
import au.com.royalpay.payment.manage.signin.beans.ChangePwdBean;
import au.com.royalpay.payment.manage.signin.core.SignInStatusManager;
@ -25,28 +29,27 @@ import au.com.royalpay.payment.tools.exceptions.ForbiddenException;
import au.com.royalpay.payment.tools.http.HttpUtils;
import au.com.royalpay.payment.tools.merchants.beans.QRCodeConfig;
import au.com.royalpay.payment.tools.merchants.beans.UpdateSurchargeDTO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import static au.com.royalpay.payment.tools.CommonConsts.RETAIL_DEVICE;
/**
@ -75,6 +78,11 @@ public class RetailAppController {
@Resource
private AttachmentClient attachmentClient;
@Autowired
private RiskBusinessService riskBusinessService;
@Autowired
private RiskUploadService riskUploadService;
@RequestMapping(value = "/token", method = RequestMethod.PUT)
public void updateDevToken(@ModelAttribute(CommonConsts.RETAIL_DEVICE) JSONObject device, @RequestBody JSONObject token) {
@ -565,4 +573,71 @@ public class RetailAppController {
retailAppService.cancelCouponAccuessLog(accuess_id, remark);
}
/**
* app
* @param clientId
* @return
*/
@GetMapping(value = "/risk/business/notice")
public JSONObject riskNotice(@RequestParam("client_id") int clientId) {
return riskBusinessService.getNoticeInfo(clientId);
}
/**
* app
* @param riskEventQuery
* @return
*/
@GetMapping(value = "/risk/business/events")
public JSONObject getRiskEvents(RiskEventQuery riskEventQuery) {
JSONObject params = riskEventQuery.toJSON();
params.put("client_moniker", riskEventQuery.getClientMoniker());
params.put("is_send_client", 1);
params.putIfAbsent("page", 1);
List<Integer> resultTypes = Arrays.asList(RiskResultTypeEnum.SEND_EMAIL_TO_BD.getResultType(),
RiskResultTypeEnum.WAIT_FOR_AUDIT.getResultType(),
RiskResultTypeEnum.MATERIAL_AUDIT_PASS.getResultType(),
RiskResultTypeEnum.MATERIAL_NOT_PASS.getResultType(),
RiskResultTypeEnum.ALREADY_HANDLED.getResultType());
params.put("result_types", resultTypes);
return riskBusinessService.getRiskEventsByPage(params, null);
}
/**
* app
* @param riskId
* @return
*/
@GetMapping(value = "/risk/business/events/{risk_id}")
public JSONObject getRiskEventDetail(@PathVariable("risk_id") String riskId) {
JSONObject riskEvent = riskBusinessService.getRiskEventDetail(riskId);
List<JSONObject> tradeLogs = riskBusinessService.getRiskEventOrderList(riskEvent);
riskEvent.put("tradeLogs", tradeLogs);
return riskEvent;
}
/**
* app
* @param riskId
* @return
*/
@GetMapping(value = "/risk/business/events/{risk_id}/materials")
public JSONObject getRiskEventMaterialsRemark(@PathVariable("risk_id") String riskId) {
return riskBusinessService.getRiskEventMaterialsRemark(riskId);
}
/**
* app
* @param material
*/
@PostMapping(value = "/risk/business/events")
public void uploadMaterial(@RequestBody JSONObject material) {
riskUploadService.submitMaterial(material);
}
@RequestMapping(value = "/risk/business/upload/files", method = RequestMethod.POST)
public JSONObject uploadImage(@RequestParam MultipartFile file) throws Exception {
return attachmentClient.uploadFile(file,false);
}
}

@ -9,6 +9,7 @@ import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;
import org.apache.ibatis.annotations.Param;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.util.Date;
@ -145,4 +146,15 @@ public interface TransactionMapper {
List<JSONObject> getHfClearAmount(JSONObject params);
List<JSONObject> analysisForATOReport(@Param("clientId") int clientId, @Param("from") Date startOfMon, @Param("to") Date endOfMon);
/**
* 退退
* @param clientId
* @param systemTransactionId
* @return
*/
@AutoSql(type = SqlType.SELECT)
PageList<JSONObject> findByClientIdAndSystemTransactionId(@Param("client_id") int clientId,
@Param("system_transaction_id") String systemTransactionId,
PageBounds pageBounds);
}

@ -31,4 +31,7 @@ public interface RiskEventMapper {
@AutoSql(type = SqlType.SELECT)
JSONObject findById(@Param("risk_id") String riskId);
@AutoSql(type = SqlType.DELETE)
void deleteRiskEvent(@Param("risk_id") String risk_id);
}

@ -84,4 +84,31 @@ public interface RiskBusinessService {
* @return
*/
JSONObject getRiskMaterial(JSONObject param);
/**
* real_order_ids
*/
void completeEventRealOrderIds();
/**
*
* @param riskId
*/
void deleteRiskEvent(String riskId);
/**
*
* @param clientId
* @return
*/
JSONObject getNoticeInfo(int clientId);
/**
*
* @param riskId
* @return
*/
JSONObject getRiskEventMaterialsRemark(String riskId);
JSONObject updateIsSendClient(String riskId);
}

@ -6,6 +6,7 @@ import au.com.royalpay.payment.core.exceptions.OrderNotExistsException;
import au.com.royalpay.payment.core.exceptions.OrderNotMatchException;
import au.com.royalpay.payment.manage.mappers.log.AppMessageLogMapper;
import au.com.royalpay.payment.manage.mappers.payment.OrderMapper;
import au.com.royalpay.payment.manage.mappers.payment.TransactionMapper;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskEventMapper;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskFileMapper;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskMaterialMapper;
@ -13,6 +14,7 @@ import au.com.royalpay.payment.manage.mappers.system.ClientDeviceTokenMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientBDMapper;
import au.com.royalpay.payment.manage.notice.core.MailService;
import au.com.royalpay.payment.manage.pushMessage.APNSMessageHelper;
import au.com.royalpay.payment.manage.pushMessage.bean.AppManagerMessageBuilder;
import au.com.royalpay.payment.manage.riskbusiness.core.RiskBusinessService;
import au.com.royalpay.payment.manage.riskbusiness.enums.RiskResultTypeEnum;
@ -29,6 +31,7 @@ import au.com.royalpay.payment.tools.permission.enums.ManagerRole;
import au.com.royalpay.payment.tools.exceptions.ServerErrorException;
import au.com.royalpay.payment.tools.threadpool.RoyalThreadPoolExecutor;
import au.com.royalpay.payment.tools.utils.PageListUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.miemiedev.mybatis.paginator.domain.Order;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
@ -45,6 +48,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
@ -77,6 +81,8 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
@Resource
private OrderMapper orderMapper;
@Resource
private TransactionMapper transactionMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private SpringTemplateEngine thymeleaf;
@ -99,6 +105,14 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
private Map<String, AppMsgSender> senderMap = new HashMap<>();
@Resource
private APNSMessageHelper apnsMessageHelper;
@Resource
public void setAppMsgSenders(AppMsgSender[] senders) {
Arrays.stream(senders).forEach(appMsgSender -> senderMap.put(appMsgSender.devType(), appMsgSender));
}
private ThreadPoolExecutor sendingAppleMsgPool = new ThreadPoolExecutor(10, 30, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
@Override
@ -110,7 +124,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
public JSONObject getRiskEventsByPage(JSONObject params, JSONObject manager) {
// 如果登录的角色是BD添加查询条件result_type为1,2,3,4order_type为1或者2
if (ManagerRole.BD_USER.hasRole(manager.getIntValue("role"))) {
if (manager != null && ManagerRole.BD_USER.hasRole(manager.getIntValue("role"))) {
params.put("bd_id", manager.getString("manager_id"));
List<Integer> orderTypes = Arrays.asList(RiskOrderTypeEnum.WECHAT_ORDER.getOrderType(),
RiskOrderTypeEnum.ALIPAY_ORDER.getOrderType());
@ -146,21 +160,20 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
@Override
public List<JSONObject> getRiskEventOrderList(JSONObject riskEvent) {
String orderIds = riskEvent.getString("order_ids");
//String orderIds = riskEvent.getString("order_ids");
String realOrderIds = riskEvent.getString("real_order_ids");
JSONObject client = clientMapper.findClientByMonikerAll(riskEvent.getString("client_moniker"));
List<JSONObject> tradeLogs = new ArrayList<>();
/**
* client
*/
if (client != null && StringUtils.isNotBlank(orderIds)) {
String[] orderIdArray = orderIds.trim().split(",");
if (client != null && StringUtils.isNotBlank(realOrderIds)) {
String[] orderIdArray = realOrderIds.trim().split(",");
JSONObject orderInfo = new JSONObject();
String realOrderId = "";
// 获取订单信息
if (riskEvent.getIntValue("order_type") == 3) {
for (int i = 0; i < orderIdArray.length; i++) {
realOrderId = orderMapper.findOrderById(orderIdArray[i],client.getIntValue("client_id")).getString("order_id");
orderInfo = tradeLogService.getOrderDetail(new JSONObject(), riskEvent.getString("client_moniker"), realOrderId, null);
orderInfo = tradeLogService.getOrderDetail(new JSONObject(), riskEvent.getString("client_moniker"), orderIdArray[i], null);
tradeLogs.add(orderInfo);
}
} else {
@ -173,8 +186,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
return tradeLogs;
}
@Override
public void addRiskEvent(JSONObject params, JSONObject manager) {
private JSONObject getEvent(JSONObject params) {
// 通用号调单不需要填写client_moniker
JSONObject client = null;
String clientMoniker = params.getString("client_moniker");
@ -185,8 +197,6 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
}
}
params.put("fillin_id", manager.getString("manager_id"));
params.put("fillin_person", manager.getString("display_name"));
String orderIds = params.getString("order_ids");
if (StringUtils.isNotBlank(orderIds)) {
// 去除所有空格与中文逗号''
@ -195,6 +205,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
params.put("order_ids", orderIds);
String[] orderIdArray = orderIds.split(",");
List<String> orderAmountList = new ArrayList<>();
List<String> realOrderIdList = new ArrayList<>();
/**
* client_monikerclient_id
* clientorder_amounts
@ -202,25 +213,40 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
*/
if (client != null) {
for (int i = 0; i < orderIdArray.length; i++) {
JSONObject orderInfo = orderMapper.findOrderById(orderIdArray[i],client.getIntValue("client_id"));
PageList<JSONObject> transactionList = transactionMapper.findByClientIdAndSystemTransactionId(client.getIntValue("client_id"), orderIdArray[i], new PageBounds(Order.formString("transaction_time.desc")));
// 判断该笔订单是否存在,是否属于该商户
if (orderInfo == null)
throw new OrderNotExistsException();
// 由于查询订单时已经关联商户了,所以只会抛出订单不匹配的异常
if (transactionList == null || transactionList.size() <= 0)
throw new OrderNotMatchException();
/*
else {
if (!clientMoniker.equals(orderInfo.getString("partner_code"))) {
if (!clientMoniker.equals(orderInfo.getString("client_moniker"))) {
throw new OrderNotMatchException();
}
orderAmountList.add(orderInfo.getString("total_amount"));
}
*/
// 将订单order_id存入数据库方便后面快速查询
JSONObject orderInfo = transactionList.get(0);
realOrderIdList.add(orderInfo.getString("order_id"));
orderAmountList.add(orderInfo.getString("transaction_amount"));
}
params.put("order_amounts", StringUtils.join(orderAmountList, ","));
params.put("real_order_ids", StringUtils.join(realOrderIdList, ","));
}
}
return params;
}
@Override
public void addRiskEvent(JSONObject params, JSONObject manager) {
params = getEvent(params);
riskEventMapper.save(params);
}
@Override
public void updateRiskEvent(JSONObject params) {
params = getEvent(params);
riskEventMapper.update(params);
}
@ -277,7 +303,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
event.put("result_type", RiskResultTypeEnum.SEND_EMAIL_TO_BD.getResultType());
event.put("submit_url",uploadUrl);
Integer orderType = event.getInteger("order_type");
if (orderType == RiskOrderTypeEnum.WARNING_ORDER.getOrderType()) {
if (orderType.equals(RiskOrderTypeEnum.WARNING_ORDER.getOrderType())) {
event.put("result_type", RiskResultTypeEnum.ALREADY_HANDLED.getResultType());
}
riskEventMapper.update(event);
@ -288,29 +314,29 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
if(event.getIntValue("order_type")==3){
sendAppRiskMessage(event);
}
}
private void sendAppRiskMessage(JSONObject event){
JSONObject client = clientMapper.findClientByMoniker(event.getString("client_moniker"));
logger.debug("sendRiskAppMessage-" + client.getString("client_moniker") + "-" + "risk_id:"+event.getString("risk_id"));
List<JSONObject> tokens = clientDeviceTokenMapper.listTokensByClient_id(client.getIntValue("client_id"));
for (final JSONObject devToken : tokens) {
for (JSONObject devToken : tokens) {
Runnable task = () -> {
String token = devToken.getString("token");
if (token == null) {
if (token == null || devToken.getString("client_type") == null) {
return;
}
JSONObject log = saveAppMessageLog(devToken.getString("dev_id"), devToken.getIntValue("client_id"), "risk", token,
JSONObject log = saveAppMessageLog(devToken.getString("dev_id"), devToken.getIntValue("client_id"), "risk" + devToken.getString("client_type"), token,
event.getString("risk_id"));
try {
event.put("send_type", "risk");
JSONObject type = new JSONObject();
type.put("send_type", "risk");
type.put("id", event.getString("risk_id"));
// apnsMessageHelper.sendAppleMessageDetail(
// LocaleSupport.localeMessage("app.message.title.risk"), LocaleSupport.localeMessage("app.message.body.risk"),
// token, event, type);
AppMsgSender sender = senderMap.get(devToken.getString("client_type"));
if (token == null || sender == null) {
if (StringUtils.isBlank(token) || sender == null) {
return;
}
JSONObject managerMsg = new JSONObject();
@ -318,7 +344,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
managerMsg.put("body",
LocaleSupport.localeMessage("app.message.body.risk"));
managerMsg.put("type", type);
managerMsg.put("data", event);
managerMsg.put("data", new JSONObject());
managerMsg.put("msgType", "risk");
AppMessage appMessage = new AppManagerMessageBuilder(managerMsg).buildMessage();
sender.sendMessage(appMessage, devToken);
@ -327,7 +353,7 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
} catch (Exception e) {
logger.error("出错了:" + e.getMessage());
appMessageLogMapper.updateStatus(log.getString("send_id"), 1, e.getMessage());
throw new ServerErrorException("Send App " + devToken.getString("client_type") + " Failed", e);
throw new ServerErrorException("Send App " + devToken.getString("client_type") + " Failed" + ",token" + token, e);
}
};
sendingAppleMsgPool.execute(task);
@ -444,12 +470,14 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
ctx.setVariable("uploadUrl", uploadUrl);
ctx.setVariable("royalpay_order_type", event.getIntValue("royalpay_order_type"));
ctx.setVariable("warning_order_type", event.getIntValue("warning_order_type"));
String[] orderIds = event.getString("order_ids").split(",");
ctx.setVariable("description", event.getString("description"));
//String[] orderIds = event.getString("order_ids").split(",");
String[] realOrderIds = event.getString("real_order_ids").split(",");
List<JSONObject> orders = new ArrayList();
switch (event.getIntValue("order_type")){
case 1:
case 2:
for(String orderId : orderIds){
for(String orderId : realOrderIds){
JSONObject order = orderMapper.findOrderById(orderId,client.getIntValue("client_id"));
if(order==null){
throw new BadRequestException("Order: "+orderId+" not exists");
@ -461,10 +489,8 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
ctx.setVariable("emailsTos", bdEmails);
break;
case 3:
String realOrderId = "";
for(String orderId : orderIds){
realOrderId = orderMapper.findOrderById(orderId,client.getIntValue("client_id")).getString("order_id");
JSONObject order = tradeLogService.getOrderDetail(new JSONObject(), clientMoniker, realOrderId, null);
for(String orderId : realOrderIds){
JSONObject order = tradeLogService.getOrderDetail(new JSONObject(), clientMoniker, orderId, null);
if(order==null){
throw new BadRequestException("Order: "+orderId+" not exists");
}
@ -522,16 +548,43 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
return null;
}
@Override
@Transactional
public void completeEventRealOrderIds() {
List<JSONObject> riskEventList = getRiskEvents(null);
if (riskEventList != null && riskEventList.size() > 0) {
for (JSONObject riskEvent : riskEventList) {
String clientMoniker = riskEvent.getString("client_moniker");
JSONObject client = clientMapper.findClientByMonikerAll(clientMoniker);
String systemTransactionIds = riskEvent.getString("order_ids");
if (StringUtils.isBlank(systemTransactionIds))
continue;
String[] systemTransactionIdArr = riskEvent.getString("order_ids").split(",");
List<String> realOrderIds = new ArrayList<>();
if (client == null)
continue;
for (int i = 0; i < systemTransactionIdArr.length; i++) {
PageList<JSONObject> transactionList = transactionMapper.findByClientIdAndSystemTransactionId(
client.getIntValue("client_id"),
systemTransactionIdArr[i],
new PageBounds(Order.formString("transaction_time.desc")));
if (transactionList != null && transactionList.size() > 0)
realOrderIds.add(transactionList.get(0).getString("order_id"));
}
riskEvent.put("real_order_ids", StringUtils.join(realOrderIds, ","));
//updateRiskEvent(riskEvent);
riskEventMapper.update(riskEvent);
}
}
}
private byte[] generateRiskOrders(JSONObject event) throws IOException {
String[] orderIds = event.getString("order_ids").split(",");
JSONObject client = clientMapper.findClientByMonikerAll(event.getString("client_moniker"));
Workbook wb = new XSSFWorkbook();
String realOrderId = "";
for(String orderId : orderIds){
realOrderId = orderMapper.findOrderById(orderId,client.getIntValue("client_id")).getString("order_id");
JSONObject orderDetail = tradeLogService.getOrderDetail(new JSONObject(), event.getString("client_moniker"), realOrderId, null);
JSONObject orderDetail = tradeLogService.getOrderDetail(new JSONObject(), event.getString("client_moniker"), orderId, null);
Sheet sheet = wb.createSheet(orderId);
sheet.setDefaultColumnWidth((short) 40);
Row row0 = sheet.createRow(0);
@ -648,6 +701,11 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
@Override
public void checkTodo(JSONObject manager, List<TodoNotice> notices) {
/**
* 1BD
* 2BD
* 3
*/
if (ManagerRole.BD_USER.hasRole(manager.getIntValue("role"))) {
JSONObject params = new JSONObject();
params.put("bd_id", manager.getString("manager_id"));
@ -677,4 +735,104 @@ public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodo
}
}
}
@Override
public void deleteRiskEvent(String riskId) {
riskEventMapper.deleteRiskEvent(riskId);
}
@Override
public JSONObject getNoticeInfo(int clientId) {
JSONObject client = clientMapper.findClient(clientId);
if(client == null){
throw new InvalidShortIdException();
}
JSONObject params = new JSONObject();
params.put("is_send_client", 1);
List<Integer> resultTypes = Arrays.asList(RiskResultTypeEnum.SEND_EMAIL_TO_BD.getResultType(),
RiskResultTypeEnum.MATERIAL_NOT_PASS.getResultType());
params.put("result_types", resultTypes);
PageList<JSONObject> riskEventList = riskEventMapper.listRisksByPage(params, new PageBounds(Order.formString("create_time.desc")));
JSONObject result = new JSONObject();
result.put("notice_flag", false);
if (riskEventList != null && riskEventList.size() > 0) {
result.put("notice_flag", true);
}
return result;
}
@Override
public JSONObject getRiskEventMaterialsRemark(String riskId) {
JSONObject riskEvent = riskEventMapper.findById(riskId);
Integer orderType = riskEvent.getInteger("order_type");
List<JSONObject> materialItemList = new ArrayList<>();
List<String> materialsRemark = new ArrayList<>();
if (orderType.equals(RiskOrderTypeEnum.WECHAT_ORDER.getOrderType())) {
materialsRemark = Arrays.asList(
"1、物流公司发货单据照片 要求:每笔交易对应的物流单必须提供,且单据必须清晰可见\n" +
"Photos of logistics companies goods-delivery documents Requirement: The logistics order record corresponding to each transaction must be provided, and details of records should be clearly visible. ",
"2、用户购买虚拟物品需要提供聊天记录、订单信息、发货凭证截图、 虚拟物品最终消费场景(例如何种游戏、软件)、提供消费场景网址/下载链接 要求:每笔交易对应的截图必须清晰可见\n" +
"Users need to provide chat records, order information, screenshots of delivery documents, final consumption scenarios of virtual goods (such as games, software); provide consumer scene URL / download link. Requirement: The screenshot corresponding to each transaction must be clearly visible. ",
"3、购物小票/发票存根照片 照片应清晰,必须显示商户名称、商户地址、购物时间、物品名称 购物金额等\n" +
"Photos of shopping receipts/ invoice stubs Requirement: The photos should be clear and must show Merchant name, Business address, Transaction time, Product information, Quantity purchased, etc. ",
"4、显示商户门牌号码和店头名称的照片 要求:清晰可见,至少一张\n" +
"Photos of Merchant Street number & Merchant name Requirement: At least one visible photo ",
"5、显示商户营业场景所场内部情况如店内商品陈列、收银台等的照片 要求:照片清晰,且能清楚显示商户实际售卖物品或服务,至少三张\n" +
"Photos of internal environment of merchant business (such as in-store merchandise display, checkout counter, etc.) Requirements: The photos (at least three) showing merchant activities including actual selling-goods or services obviously ",
"6、其他图片\n" +
"Other pictures "
);
} else if (orderType.equals(RiskOrderTypeEnum.ALIPAY_ORDER.getOrderType())) {
String alipayMaterials = riskEvent.getString("alipay_materials");
if (StringUtils.isNotBlank(alipayMaterials)) {
List<JSONObject> alipayMaterialRemarkList = JSONObject.parseArray(alipayMaterials, JSONObject.class);
if (alipayMaterialRemarkList != null && alipayMaterialRemarkList.size() > 0) {
for (int i = 0; i < alipayMaterialRemarkList.size(); i++) {
materialsRemark.add(alipayMaterialRemarkList.get(i).getString("question" + (i + 1)));
}
}
}
} else if (orderType.equals(RiskOrderTypeEnum.ROYALPAY_ORDER.getOrderType())) {
materialsRemark = Arrays.asList(
"1.请解释相应的消费场景/业务模式,例如网站商城,扫码支付, 消费者到店支付等;\n" +
"Please explain the relative payment scenario/business activities, for example, online store, QR code payment, payment at the store, etc;",
"2.提供相应购物清单,订单小票(请提供与被查交易订单号相匹配的交易时间及金额的发票);\n" +
"Provide related shopping lists, invoices. (Please provide the invoices, amount of which matches that of the abnormal transaction);",
"3.提供相应的发货证明,报关单(若有消费者在国内购买,请提供物流单据或报关单);\n" +
"Relative proof of delivery, customs declaration (If the consumer purchased from China, please provide shipping receipt or customs declaration);",
"4.提供您的门店照片(门店照及店铺内的照片各一张, 一张可以看到商户名的门头照,一张可以看到相关商品或服务的店内照片);\n" +
"Photos of the store ( one of each front-store and in-store);",
"5.其他可以还原交易背景的资料,如和消费者的聊天记录等,来佐证被查单号交易的真实性;\n" +
"Other materials that can verify the payment scenario, for example, chatting history, to prove the truth of the transactions;",
"6、其他图片\n" +
"Other pictures"
);
}
JSONObject material = getRiskMaterial(riskEvent);
for (int i = 0; i < materialsRemark.size(); i++) {
JSONObject materialItem = new JSONObject();
materialItem.put("question", materialsRemark.get(i));
if (material != null)
materialItem.put("file", material.getJSONArray("file" + (i + 1)));
materialItemList.add(materialItem);
}
JSONObject result = new JSONObject();
result.put("material", materialItemList);
if (material != null && material.containsKey("description"))
result.put("description", material.getString("description"));
return result;
}
@Override
public JSONObject updateIsSendClient(String riskId) {
JSONObject event = riskEventMapper.findById(riskId);
event.put("is_send_client", 1);
riskEventMapper.update(event);
event = riskEventMapper.findById(riskId);
return event;
}
}

@ -1,24 +1,17 @@
package au.com.royalpay.payment.manage.riskbusiness.core.impl;
import au.com.royalpay.payment.core.exceptions.EmailException;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskEventMapper;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskFileMapper;
import au.com.royalpay.payment.manage.mappers.riskbusiness.RiskMaterialMapper;
import au.com.royalpay.payment.manage.mappers.system.ClientMapper;
import au.com.royalpay.payment.manage.notice.core.MailService;
import au.com.royalpay.payment.manage.riskbusiness.core.RiskBusinessService;
import au.com.royalpay.payment.manage.riskbusiness.core.RiskUploadService;
import au.com.royalpay.payment.manage.riskbusiness.enums.RiskResultTypeEnum;
import au.com.royalpay.payment.tools.exceptions.BadRequestException;
import au.com.royalpay.payment.tools.threadpool.RoyalThreadPoolExecutor;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring4.SpringTemplateEngine;
import javax.annotation.Resource;
import java.text.ParseException;
@ -40,6 +33,7 @@ public class RiskUploadServiceIpml implements RiskUploadService {
@Override
public void submitMaterial(JSONObject material) {
JSONObject event = riskEventMapper.findById(material.getString("risk_id"));
material.putIfAbsent("update_time", DateFormatUtils.format(new Date(), "yyyy-MM-dd hh:mm:ss"));
riskMaterialMapper.save(material);
material.put("material_id",riskMaterialMapper.findAllMaterials(material.getString("risk_id")).get(0).getString("material_id"));
for(int i=1;i<=10;i++){

@ -739,17 +739,10 @@
</where>
</select>
<!--
由于当时定的是order_id, 所以是根据order_id查的
现在要改成依据system_transaction_id查
直接改成t.system_transaction_id = #{order_id}
否则要改的地方太多了,
所以这里的参数order_id其实是system_transaction_id
特此说明!
-->
<select id="findOrderById" resultType="com.alibaba.fastjson.JSONObject">
SELECT
o.order_id order_id,
t.system_transaction_id,
o.currency,
o.channel,
o.create_time,
@ -808,8 +801,9 @@
WHEN 14
THEN '账单链接'
END AS gateway,
p.client_moniker partner_code,
p.client_moniker client_moniker,
p.short_name partner_name,
p.company_name,
format(t.clearing_amount,2) clearing_amount
FROM pmt_orders o
INNER JOIN sys_clients p

@ -23,6 +23,9 @@
re.in_user_blacklist,
re.processed_remark,
re.create_time,
re.alipay_materials,
re.is_send_client,
re.real_order_ids,
sc.industry,
sc.short_name,
IFNULL(sc.sub_merchant_id, re.sub_merchant_id) sub_merchant_id
@ -66,10 +69,15 @@
<if test="risk_id != null">
AND re.risk_id = #{risk_id}
</if>
<if test="client_moniker != null">
AND re.client_moniker = #{client_moniker}
</if>
<if test="is_send_client != null">
AND re.is_send_client = #{is_send_client}
</if>
<if test="order_types != null">
<foreach collection="order_types" item="order_type_item" open="and re.order_type in (" close=")" separator=",">
#{order_type_item}
@ -93,19 +101,26 @@
<if test="order_ids != null">
AND re.order_ids LIKE CONCAT('%', #{order_ids}, '%')
</if>
<if test="sub_merchant_id != null">
AND sc.sub_merchant_id = #{sub_merchant_id}
</if>
<if test="industry != null">
AND sc.industry = #{industry}
</if>
<if test="receive_email_date != null">
AND re.receive_email_date = #{receive_email_date}
</if>
<if test="reply_email_date != null">
AND re.reply_email_date = #{reply_email_date}
</if>
<if test="real_order_ids != null">
AND re.real_order_ids = #{real_order_ids}
</if>
</where>
</select>
</mapper>

@ -88,7 +88,7 @@ todo.bd.order=The merchant managed by you is transferred. Please submit the mate
app.message.title.payment=Payment Message
app.message.body.payment=You have received a payment of
app.message.title.risk=Risk Control Message
app.message.body.risk=You have received a risk control reminder. Please check the email for details.
app.message.body.risk=You have received a risk control reminder. Please handle as soon as possible.
app.message.title.notice=System Message
app.message.body.refund=Your refund request has bean sent,refund count is
app.message.body.cashback=You got a cashback of

@ -84,7 +84,7 @@ todo.bd.order=有商户被调单,请尽快提交材料
app.message.title.payment=到账通知
app.message.body.payment=您有一笔新的到账,
app.message.title.risk=风控通知
app.message.body.risk=您收到了一条风控提醒,详情请查看邮件
app.message.body.risk=您收到了一条风控提醒,请及时处理
app.message.title.notice=系统通知
app.message.body.refund=退款申请已提交渠道方处理,退款金额
app.message.body.cashback=您获得了一笔ROYALPAY返现金额

@ -161,13 +161,15 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
}
]);
app.controller('riskEventDetailCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'Upload', 'commonDialog', 'riskEvent',
function ($scope, $state, $http, $uibModal, $filter, Upload, commonDialog, riskEvent) {
app.controller('riskEventDetailCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'Upload', 'commonDialog', 'riskEvent', 'orderService',
function ($scope, $state, $http, $uibModal, $filter, Upload, commonDialog, riskEvent, orderService) {
$scope.orderTypes = orderTypesMap;
$scope.resultTypes = resultTypesMap;
$scope.channelResults = channelResultArray;
$scope.riskEvent = riskEvent.data;
//var index = $scope.riskEvent.order_ids.lastIndexOf(",");
//$scope.riskEvent.order_ids = $scope.riskEvent.order_ids.substring(0, index) + "" + $scope.riskEvent.order_ids.substring(index + 1);
// 获取数据库中对应的渠道字段
var orderChannel = 'enable_';
if ($scope.riskEvent.order_type == 1) {
@ -279,6 +281,7 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
})
};
// 发送email
$scope.resendUploadEmail = function () {
commonDialog.confirm({
title: 'Warning',
@ -295,6 +298,10 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
})
};
// 订单详情
$scope.showTradeDetail = function (order) {
orderService.managerOrderDetail(order)
};
// 以下为BD上传材料相关
$scope.material={};
@ -535,6 +542,36 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
$scope.orderTypes = orderTypesMap;
$scope.royapayOrderTypes = royalpayOrderTypesMap;
$scope.warningOrderTypes = warningOrderTypesMap;
$scope.materials = [{key: 0, value: ""}];
$scope.canMinus = false;
$scope.increase = function($index) {
$scope.materials.splice($index + 1, 0,
{key: new Date().getTime(), value: ""}); // 用时间戳作为每个item的key
// 增加新的input后允许删除
$scope.canMinus = true;
}
$scope.decrease = function($index) {
// 如果input大于1删除
if ($scope.materials.length > 1) {
$scope.materials.splice($index, 1);
}
// 如果回复数为1不允许删除
if ($scope.materials.length == 1) {
$scope.canMinus = false;
}
}
var array=new Array();
$scope.combineMaterials = function() {
for (var i = 0; i < $scope.materials.length; i++) {
var cr = {};
cr['question'+(i+1)] = $scope.materials[i].value;
array[i] = cr;
}
console.log(JSON.stringify(array));
return JSON.stringify(array);
}
$scope.save = function(form) {
if (form.$invalid) {
@ -558,6 +595,8 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
$scope.riskEvent.receive_email_date = $filter('date')($scope.riskEvent.receive_email_date, 'yyyy-MM-dd');
$scope.riskEvent.alipay_materials = $scope.combineMaterials();
$http.post('/risk/business/events', $scope.riskEvent).then(function (resp) {
commonDialog.alert({
title: 'Success',
@ -579,7 +618,9 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'],
$http.get('/risk/business/partners', {params: $scope.partnerParam}).then(function (resp) {
$scope.partners = resp.data;
if ($scope.partners != null && $scope.partners.length > 0) {
$scope.riskEvent.client_moniker = $scope.partners[0].client_moniker;
// 由于通用号调单可能无法提供商户client_moniker,所以后台无法查询到订单
if ($scope.partners.length == 1 || $scope.riskEvent.order_type != 5)
$scope.riskEvent.client_moniker = $scope.partners[0].client_moniker;
}
else {
commonDialog.confirm({

@ -158,15 +158,17 @@
ng-if="riskEvent.order_type != 4"
ng-class="{'has-error':riskEventForm.order_ids.$invalid && riskEventForm.order_ids.$dirty}">
<label class="control-label col-sm-2"
for="order-ids-input">Order IDs</label>
for="order-ids-input">Platform Transaction IDs</label>
<div class="col-sm-8">
<input class="form-control"
<textarea class="form-control"
ng-model="riskEvent.order_ids"
type="text"
rows="2"
name="order_ids"
id="order-ids-input"
placeholder="order id1,order id2,order id3..."
placeholder="多个id请用半角逗号 ',' 隔开"
required>
</textarea>
<div ng-messages="riskEventForm.order_ids.$error"
ng-if="riskEventForm.order_ids.$dirty">
<p class="small text-danger"
@ -207,6 +209,30 @@
</textarea>
</div>
</div>
<div class="form-group"ng-repeat="material in materials"
ng-if="riskEvent.order_type == 2"
ng-class="{'has-error':riskEventForm.client_moniker.$invalid && riskEventForm.client_moniker.$dirty}">
<label class="control-label col-sm-2"
for="material{{$index+1}}">Material{{$index + 1}}
</label>
<div class="col-sm-8">
<input class="form-control"
ng-model="material.value"
type="text"
name="material{{$index+1}}"
id="material{{$index+1}}"
required
ng-pattern="/^[a-zA-Z0-9]+$/"
>
</div>
<div class="col-sm-2">
<a class="text-success" ng-click="increase($index)"><i class="fa fa-plus-circle" style="width: 30px"></i></a>
<a class="text-danger" ng-click="decrease($index)" ng-show="canMinus"><i class="fa fa-minus-circle" style="width: 30px"></i></a>
</div>
</div>
</div>
</div>
</div>

@ -43,6 +43,14 @@
</div>
</div>
<div class="form-group" ng-if="clientInfo.company_name != null">
<label class="control-label col-sm-2">Company Name</label>
<div class="col-sm-10">
<p class="form-control-static"
ng-bind="clientInfo.company_name"></p>
</div>
</div>
<div class="form-group" ng-if="riskEvent.order_type == 1 || riskEvent.order_type == 5">
<label class="control-label col-sm-2">Sub Merchant ID</label>
<div class="col-sm-10">
@ -53,10 +61,10 @@
</div>
<div class="form-group" ng-if="riskEvent.order_ids != null">
<label class="control-label col-sm-2">Order IDs</label>
<div class="col-sm-10">
<p class="form-control-static"
ng-bind="riskEvent.order_ids">
<label class="control-label col-sm-2">Platform Transaction IDs</label>
<div class="col-sm-6">
<p class="form-control-static">
{{riskEvent.order_ids}}
</p>
</div>
</div>
@ -227,8 +235,8 @@
<thead>
<tr>
<th>Partner</th>
<th>Order ID</th>
<th>Platform Transaction ID</th>
<th>Order ID</th>
<th>Order Description</th>
<th>Customer ID</th>
<th>IP</th>
@ -247,8 +255,8 @@
<tbody>
<tr ng-repeat="trade in tradeLogs">
<td>{{trade.client.short_name}}({{trade.client.client_moniker}})</td>
<td>{{trade.order_id}}</td>
<td>{{trade.system_transaction_id}}</td>
<td>{{trade.order_id}}</td>
<td>{{trade.order_description}}</td>
<td>{{trade.customer_id}}</td>
<td>{{trade.customer_ip}}</td>
@ -267,6 +275,7 @@
<table class="table table-bordered table-hover table-striped" ng-if="riskEvent.order_type != 3">
<thead>
<tr>
<th>Platform Transaction ID</th>
<th>Order ID</th>
<th>Amount</th>
<th>Input Amount</th>
@ -275,24 +284,31 @@
<th>Status</th>
<th>Create Time</th>
<th>Gateway</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="trade in tradeLogs"
ng-class="{warning:trade.clearing_status==2}">
<td>{{trade.system_transaction_id}}</td>
<td>{{trade.order_id}}</td>
<td>{{trade.currency}} {{trade.total_amount}}</td>
<td>{{trade.currency}} {{trade.display_amount}}</td>
<td>AUD {{trade.clearing_amount}}</td>
<td>
<span ng-if="(trade.channel != 'hf') && (trade.channel != 'Rpay')"
ng-bind="trade.exchange_rate">
</span>
<span ng-if="(trade.channel != 'hf') && (trade.channel != 'Rpay')"
ng-bind="trade.exchange_rate">
</span>
<span ng-if="(trade.channel == 'hf') || (trade.channel == 'Rpay')"> - </span>
</td>
<td ng-bind="trade.status"></td>
<td ng-bind="trade.create_time"></td>
<td ng-bind="trade.gateway"></td>
<td>
<a role="button" class="text-bold" ng-click="showTradeDetail(trade)" title="Detail">
<i class="fa fa-list-alt"></i>
</a>
</td>
</tr>
</tbody>
</table>
@ -389,7 +405,7 @@
ng-if="riskEventEdit.order_type != 4"
ng-class="{'has-error':riskEventForm.order_ids.$invalid && riskEventForm.order_ids.$dirty}">
<label class="control-label col-sm-2"
for="order-ids-input">Order IDs
for="order-ids-input">Platform Transaction IDs
</label>
<div class="col-sm-8">
<input class="form-control"

@ -56,10 +56,10 @@
</div>
</div>
<!--Order IDs-->
<!--Platform Transaction IDs-->
<div class="form-group col-sm-6">
<label class="control-label col-xs-4 col-sm-4"
for="order-ids-input">Order IDs
for="order-ids-input">Platform Transaction IDs
</label>
<div class="col-xs-6">
<input class="form-control"
@ -200,10 +200,14 @@
</td>
<td ng-bind="riskEvent.order_type | orderType"></td>
<td>
{{riskEvent.result_type | resultType:resultSearchTypes}}
<span ng-if="riskEvent.order_type == 5">-</span>
<span ng-if="riskEvent.order_type != 5">{{riskEvent.result_type | resultType:resultSearchTypes}}</span>
</td>
<td ng-bind="riskEvent.sub_merchant_id"></td>
<td ng-bind="riskEvent.email_status | emailStatus"></td>
<td>
<span ng-if="riskEvent.order_type != 5">{{riskEvent.email_status | emailStatus}}</span>
<span ng-if="riskEvent.order_type == 5">-</span>
</td>
<td ng-bind="riskEvent.description | limitTo:20"></td>
<td ng-bind="riskEvent.channel_result"></td>

Loading…
Cancel
Save