diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskEventMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskEventMapper.java index 62c23ea2c..d27b4351b 100644 --- a/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskEventMapper.java +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskEventMapper.java @@ -4,8 +4,12 @@ import cn.yixblog.support.mybatis.autosql.annotations.AutoMapper; import cn.yixblog.support.mybatis.autosql.annotations.AutoSql; import cn.yixblog.support.mybatis.autosql.annotations.SqlType; import com.alibaba.fastjson.JSONObject; +import com.github.miemiedev.mybatis.paginator.domain.PageBounds; +import com.github.miemiedev.mybatis.paginator.domain.PageList; import org.apache.ibatis.annotations.Param; +import java.util.List; + /** * @Author lvjian * @Date 2018/10/10 10:25 @@ -19,6 +23,11 @@ public interface RiskEventMapper { @AutoSql(type = SqlType.UPDATE) void update(JSONObject riskEvent); + @AutoSql(type = SqlType.SELECT) + List findAll(JSONObject params); + + PageList listRisksByPage(JSONObject params, PageBounds pageBounds); + @AutoSql(type = SqlType.SELECT) JSONObject findById(@Param("risk_id") String riskId); diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskFileMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskFileMapper.java new file mode 100644 index 000000000..6120db3cd --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskFileMapper.java @@ -0,0 +1,18 @@ +package au.com.royalpay.payment.manage.mappers.riskbusiness; + +import cn.yixblog.support.mybatis.autosql.annotations.AutoMapper; +import cn.yixblog.support.mybatis.autosql.annotations.AutoSql; +import cn.yixblog.support.mybatis.autosql.annotations.SqlType; +import com.alibaba.fastjson.JSONObject; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@AutoMapper(tablename = "risk_file", pkName = "file_id") +public interface RiskFileMapper { + @AutoSql(type= SqlType.INSERT) + void save(JSONObject file); + + @AutoSql(type= SqlType.SELECT) + List findAllFiles(@Param("material_id") String material_id); +} diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.java new file mode 100644 index 000000000..f5e9f5b9f --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.java @@ -0,0 +1,20 @@ +package au.com.royalpay.payment.manage.mappers.riskbusiness; + + +import cn.yixblog.support.mybatis.autosql.annotations.AutoMapper; +import cn.yixblog.support.mybatis.autosql.annotations.AutoSql; +import cn.yixblog.support.mybatis.autosql.annotations.SqlType; +import com.alibaba.fastjson.JSONObject; +import org.apache.ibatis.annotations.Param; + + +import java.util.List; + +@AutoMapper(tablename = "risk_material", pkName = "material_id") +public interface RiskMaterialMapper { + @AutoSql(type = SqlType.INSERT) + void save(JSONObject material); + + List findAllMaterials(@Param("risk_id") String risk_id); + +} diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.java index 09bc51de0..b8e390f0b 100644 --- a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.java +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.java @@ -25,6 +25,8 @@ public interface ClientBDMapper { List listClientBDInfoAvailable(@Param("client_id") int clientId, @Param("date") Date date); + List listBDClientInfo(JSONObject param); + void updateClientDB(JSONObject jsonObject); @AutoSql(type = SqlType.UPDATE) diff --git a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java index 4ade2bd6a..4e48045ce 100644 --- a/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java +++ b/src/main/java/au/com/royalpay/payment/manage/mappers/system/ClientMapper.java @@ -36,6 +36,10 @@ public interface ClientMapper { @AutoSql(type = SqlType.SELECT) JSONObject findClientByMonikerAll(@Param("client_moniker") String clientMoniker); + @AutoSql(type = SqlType.SELECT) + @AdvanceSelect(addonWhereClause = "is_valid=1") + List getClientBySubMerchantId(@Param("sub_merchant_id") String subMerchantId); + PageList listPartners(JSONObject params, PageBounds pagination); PageList comListPartners(JSONObject params, PageBounds pagination); diff --git a/src/main/java/au/com/royalpay/payment/manage/merchants/core/ClientManager.java b/src/main/java/au/com/royalpay/payment/manage/merchants/core/ClientManager.java index f19d10018..1c21c90fd 100644 --- a/src/main/java/au/com/royalpay/payment/manage/merchants/core/ClientManager.java +++ b/src/main/java/au/com/royalpay/payment/manage/merchants/core/ClientManager.java @@ -351,4 +351,6 @@ public interface ClientManager { boolean postponeClientRate(int clientId, String clientMoniker,String nextYearExipryDate); JSONObject comListPartnerSelection(JSONObject manager, PartnerQuery query); + + List getClientBySimpleQuery(JSONObject param); } diff --git a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java index 0520f541e..40042d8cc 100644 --- a/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/merchants/core/impls/ClientManagerImpl.java @@ -4029,6 +4029,14 @@ public class ClientManagerImpl implements ClientManager, ManagerTodoNoticeProvid }); } + @Override + public List getClientBySimpleQuery(JSONObject param) { + String subMerchantId = param.getString("sub_merchant_id"); + if (StringUtils.isNotBlank(subMerchantId)) { + return clientMapper.getClientBySubMerchantId(subMerchantId); + } + return null; + } @Override @Transactional public boolean postponeClientRate(int clientId, String clientMoniker, String nextYearExipryDate) { diff --git a/src/main/java/au/com/royalpay/payment/manage/notice/core/MailService.java b/src/main/java/au/com/royalpay/payment/manage/notice/core/MailService.java index 3044b09fc..d03ea0cbe 100644 --- a/src/main/java/au/com/royalpay/payment/manage/notice/core/MailService.java +++ b/src/main/java/au/com/royalpay/payment/manage/notice/core/MailService.java @@ -17,6 +17,8 @@ public interface MailService { String sendEmail(String title, String mailTos, String mailCcs, String content, List attachFiles) throws URISyntaxException, IOException; + String sendRiskEmail(String title, String mailTos, String mailCcs, String content, List attachFiles, int order_type) throws URISyntaxException, IOException; + List checkEmailStatus(String emailId); void removeUnsub(Long id); diff --git a/src/main/java/au/com/royalpay/payment/manage/notice/core/impls/MailServiceImp.java b/src/main/java/au/com/royalpay/payment/manage/notice/core/impls/MailServiceImp.java index 63a1ed1db..a44a49cab 100644 --- a/src/main/java/au/com/royalpay/payment/manage/notice/core/impls/MailServiceImp.java +++ b/src/main/java/au/com/royalpay/payment/manage/notice/core/impls/MailServiceImp.java @@ -266,4 +266,39 @@ public class MailServiceImp implements MailService { } } + @Override + public String sendRiskEmail(String title, String mailTos, String mailCcs, String content, List attachFiles, int order_type) throws URISyntaxException, IOException { + NoticeBean noticeBean = new NoticeBean(); + noticeBean.setTitle(title); + List mailClients = new ArrayList<>(); + JSONObject mailClient = new JSONObject(); + mailClient.put("mailto", mailTos); + mailClient.put("mailcc", mailCcs); + mailClients.add(mailClient); + noticeBean.setMailClients(mailClients); + noticeBean.setContent(content); + noticeBean.setAttachFiles(attachFiles); + noticeBean.setSenderAddress("riskcontrol@royalpay.com.au"); + noticeBean.setPassword("RPrisk123"); + if(order_type == 1 || order_type == 2){ + noticeBean.setSenderAddress("risk@royalpay.com.au"); + noticeBean.setPassword("Tunnelrisk123"); + } + String postUrl = mailHost + "/mail/single?" + generateMailSignParam(); + HttpRequestResult result = null; + try { + logger.info("===sendEmail===noticeBean:" + JSON.toJSON(noticeBean)); + result = new HttpRequestGenerator(postUrl, RequestMethod.POST).setJSONEntity(noticeBean).setTimeout(60_000).execute(); + if (result.isSuccess()) { + String mail_id = result.getResponseContentJSONObj().getString("mail_id"); + return mail_id; + //System.out.println("send Mail=============="+mail_id); + } else { + throw new ServerErrorException("Error Connection"); + } + } catch (URISyntaxException e) { + throw new ServerErrorException("Error Connection"); + } + } + } diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/bean/RiskEventQuery.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/bean/RiskEventQuery.java new file mode 100644 index 000000000..29b3bb14f --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/bean/RiskEventQuery.java @@ -0,0 +1,201 @@ +package au.com.royalpay.payment.manage.riskbusiness.bean; + +import au.com.royalpay.payment.core.exceptions.ParamInvalidException; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.lang3.StringUtils; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * @Author lvjian + * @Date 2018/10/10 11:27 + */ +public class RiskEventQuery { + + // 调单类型 + private Integer orderType; + + // 商户编码 + private String clientMoniker; + + // 子商户号 + private String subMerchantId; + + // 订单号(多个,以逗号分隔) + private String orderIds; + + // 邮件发送状态 + private Integer emailStatus; + + // 行业 + private String industry; + + // 处理结果 + private Integer resultType; + + // 收到调单邮件日期 + private String receiveEmailDate; + + // 邮件回复截止日期 + private String replyEmailDate; + + private Integer page = 1; + + // 金额区间 + private String startAmount; + private String endAmount; + + public String getStartAmount() { + return startAmount; + } + + public void setStartAmount(String startAmount) { + this.startAmount = startAmount; + } + + public String getEndAmount() { + return endAmount; + } + + public void setEndAmount(String endAmount) { + this.endAmount = endAmount; + } + + public Integer getOrderType() { + return orderType; + } + + public void setOrderType(Integer orderType) { + this.orderType = orderType; + } + + public String getClientMoniker() { + return clientMoniker; + } + + public void setClientMoniker(String clientMoniker) { + this.clientMoniker = clientMoniker; + } + + public String getSubMerchantId() { + return subMerchantId; + } + + public void setSubMerchantId(String subMerchantId) { + this.subMerchantId = subMerchantId; + } + + public String getOrderIds() { + return orderIds; + } + + public void setOrderIds(String orderIds) { + this.orderIds = orderIds; + } + + public Integer getEmailStatus() { + return emailStatus; + } + + public void setEmailStatus(Integer emailStatus) { + this.emailStatus = emailStatus; + } + + public String getIndustry() { + return industry; + } + + public void setIndustry(String industry) { + this.industry = industry; + } + + public Integer getResultType() { + return resultType; + } + + public void setResultType(Integer resultType) { + this.resultType = resultType; + } + + public String getReceiveEmailDate() { + return receiveEmailDate; + } + + public void setReceiveEmailDate(String receiveEmailDate) { + this.receiveEmailDate = receiveEmailDate; + } + + public String getReplyEmailDate() { + return replyEmailDate; + } + + public void setReplyEmailDate(String replyEmailDate) { + this.replyEmailDate = replyEmailDate; + } + + public Integer getPage() { + return page; + } + + public void setPage(Integer page) { + this.page = page; + } + + public JSONObject toJSON() { + + JSONObject params = new JSONObject(); + + if (orderType != null && orderType >= 1) { + params.put("order_type", orderType); + } + + if (StringUtils.isNotBlank(clientMoniker)) { + params.put("client_moniker", clientMoniker); + } + + if (StringUtils.isNotBlank(subMerchantId)) { + params.put("sub_merchant_id", subMerchantId); + } + + if (StringUtils.isNotBlank(orderIds)) { + orderIds = orderIds.trim().replace(",", ","); + params.put("order_ids", orderIds); + } + + if (emailStatus != null && emailStatus >= 0) { + params.put("email_status", emailStatus); + } + + if (resultType != null && resultType >= 0) { + params.put("result_type", resultType); + } + + if (StringUtils.isNotBlank(industry)) { + params.put("industry", industry); + } + + if (receiveEmailDate != null) { + params.put("receive_email_date", receiveEmailDate); + } + + if (replyEmailDate != null) { + params.put("reply_email_date", replyEmailDate); + } + + if (page != null && page > 0) { + params.put("page", page); + } + + if (StringUtils.isNotBlank(startAmount)) { + params.put("start_amount", startAmount); + } + + if (StringUtils.isNotBlank(endAmount)) { + params.put("end_amount", endAmount); + } + + return params; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java index f882a2a20..94ac8396b 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskBusinessService.java @@ -1,10 +1,87 @@ package au.com.royalpay.payment.manage.riskbusiness.core; +import com.alibaba.fastjson.JSONObject; +import com.github.miemiedev.mybatis.paginator.domain.PageBounds; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; + /** * @Author lvjian * @Date 2018/10/10 10:29 */ public interface RiskBusinessService { + /** + * 风险事件列表 + * @param params + * @return + */ + List getRiskEvents(JSONObject params); + + /** + * 分页查询风险事件 + * @param params + * @return + */ + JSONObject getRiskEventsByPage(JSONObject params, JSONObject manager); + + /** + * 风险事件详情 + * @param riskId + * @return + */ + JSONObject getRiskEventDetail(String riskId); + + /** + * 获取风险事件的调单信息 + * @param orderIds + * @return + */ + List getRiskEventOrderList(String orderIds); + + /** + * 新增风险事件 + * @param params + */ + void addRiskEvent(JSONObject params, JSONObject manager); + + /** + * 更新事件 + * @param params + */ + void updateRiskEvent(JSONObject params); + + /** + * 下载审核材料(zip) + * @param riskId + */ + void downloadAuditMaterialZiP(String riskId, HttpServletResponse response); + + /** + * 发送上传材料的邮件 + * @param riskId + */ + void sendUploadEmail(String riskId) throws IOException; + + /** + * 发送拒绝邮件 + * @param riskId + */ + void sendRefuseEmail(String riskId) throws IOException; + + /** + * 发送提醒邮件 + * @param riskId + */ + void sendUrgeEmail(String riskId) throws IOException; + + /** + * 获取最新上传的审核材料 + * @param param + * @return + */ + JSONObject getRiskMaterial(JSONObject param); } diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskUploadService.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskUploadService.java new file mode 100644 index 000000000..33cc158b2 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/RiskUploadService.java @@ -0,0 +1,24 @@ +package au.com.royalpay.payment.manage.riskbusiness.core; + +import com.alibaba.fastjson.JSONObject; + +public interface RiskUploadService { + /** + * 上传材料 + * @param material + */ + void submitMaterial(JSONObject material); + + /** + * 删除缓存 + * @param codeKey + */ + void deleteUploadMailKey(String codeKey); + + /** + * + * @param codeKey + * @param risk_id + */ + void checkUploadMailKey(String codeKey,String risk_id); +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java index 9b071836e..c6755c2fb 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskBusinessServiceImpl.java @@ -1,12 +1,624 @@ package au.com.royalpay.payment.manage.riskbusiness.core.impl; +import au.com.royalpay.payment.core.exceptions.EmailException; +import au.com.royalpay.payment.core.exceptions.InvalidShortIdException; +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.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.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.merchants.beans.PartnerQuery; +import au.com.royalpay.payment.manage.notice.core.MailService; +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; +import au.com.royalpay.payment.manage.signin.beans.TodoNotice; +import au.com.royalpay.payment.manage.signin.core.ManagerTodoNoticeProvider; +import au.com.royalpay.payment.manage.tradelog.core.TradeLogService; +import au.com.royalpay.payment.manage.riskbusiness.enums.RiskOrderTypeEnum; +import au.com.royalpay.payment.tools.device.message.AppMessage; +import au.com.royalpay.payment.tools.device.message.AppMsgSender; +import au.com.royalpay.payment.tools.env.PlatformEnvironment; +import au.com.royalpay.payment.tools.exceptions.BadRequestException; +import au.com.royalpay.payment.tools.locale.LocaleSupport; +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.JSONObject; +import com.github.miemiedev.mybatis.paginator.domain.Order; +import com.github.miemiedev.mybatis.paginator.domain.PageBounds; +import com.github.miemiedev.mybatis.paginator.domain.PageList; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.slf4j.Logger; +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.thymeleaf.context.Context; +import org.thymeleaf.spring4.SpringTemplateEngine; + +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URL; +import java.util.*; +import javax.annotation.Resource; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import java.util.concurrent.TimeUnit; /** * @Author lvjian * @Date 2018/10/10 10:30 */ @Service -public class RiskBusinessServiceImpl implements RiskBusinessService { +public class RiskBusinessServiceImpl implements RiskBusinessService, ManagerTodoNoticeProvider { + + private Logger logger = LoggerFactory.getLogger(RiskBusinessServiceImpl.class); + + @Resource + private RiskEventMapper riskEventMapper; + @Resource + private ClientMapper clientMapper; + @Resource + private ClientBDMapper clientBDMapper; + @Resource + private OrderMapper orderMapper; + @Resource + private StringRedisTemplate stringRedisTemplate; + @Resource + private SpringTemplateEngine thymeleaf; + @Resource + private MailService mailService; + @Resource + private TradeLogService tradeLogService; + @Resource + private RoyalThreadPoolExecutor royalThreadPoolExecutor; + private final String UPLOAD_MAIL_PREFIX = "UPLOAD_MAIL"; + + @Autowired + private RiskMaterialMapper riskMaterialMapper; + @Resource + private RiskFileMapper riskFileMapper; + @Resource + private ClientDeviceTokenMapper clientDeviceTokenMapper; + @Resource + private AppMessageLogMapper appMessageLogMapper; + + private Map senderMap = new HashMap<>(); + + private ThreadPoolExecutor sendingAppleMsgPool = new ThreadPoolExecutor(10, 30, 5, TimeUnit.SECONDS, new LinkedBlockingQueue()); + + @Override + public List getRiskEvents(JSONObject params) { + return riskEventMapper.findAll(params); + } + + @Override + public JSONObject getRiskEventsByPage(JSONObject params, JSONObject manager) { + + // 如果登录的角色是BD,添加查询条件result_type为1,2,3,4,order_type为1或者2 + if (ManagerRole.BD_USER.hasRole(manager.getIntValue("role"))) { + params.put("bd_id", manager.getString("manager_id")); + List orderTypes = Arrays.asList(RiskOrderTypeEnum.WECHAT_ORDER.getOrderType(), + RiskOrderTypeEnum.ALIPAY_ORDER.getOrderType()); + params.put("order_types", orderTypes); + + List resultTypes = Arrays.asList(RiskResultTypeEnum.SEND_EMAIL_TO_BD.getResultType(), + RiskResultTypeEnum.WAIT_FOR_AUDIT.getResultType(), + RiskResultTypeEnum.MATERIAL_AUDIT_PASS.getResultType(), + RiskResultTypeEnum.MATERIAL_NOT_PASS.getResultType()); + params.put("result_types", resultTypes); + } + + PageList riskEvents = riskEventMapper.listRisksByPage(params, new PageBounds(params.getInteger("page"), 10, Order.formString("create_time.desc"))); + return PageListUtils.buildPageListResult(riskEvents); + } + + @Override + public JSONObject getRiskEventDetail(String riskId) { + + JSONObject riskEventDetail = riskEventMapper.findById(riskId); + // 获取商户信息 + JSONObject client = null; + String clientMoniker = riskEventDetail.getString("client_moniker"); + if (clientMoniker != null) { + client = clientMapper.findClientByMonikerAll(clientMoniker); + if (client == null) { + throw new InvalidShortIdException(); + } + } + riskEventDetail.put("clientInfo", client); + return riskEventDetail; + } + + @Override + public List getRiskEventOrderList(String orderIds) { + List tradeLogs = new ArrayList<>(); + if (StringUtils.isNotBlank(orderIds)) { + String[] orderIdArray = orderIds.trim().split(","); + JSONObject query = new JSONObject(); + // 获取订单信息 + for (int i = 0; i < orderIdArray.length; i++) { + JSONObject orderInfo = orderMapper.findOrderById(orderIdArray[i]); + tradeLogs.add(orderInfo); + } + } + return tradeLogs; + } + + @Override + public void addRiskEvent(JSONObject params, JSONObject manager) { + // 通用号调单不需要填写client_moniker + JSONObject client = null; + String clientMoniker = params.getString("client_moniker"); + if (clientMoniker != null) { + client = clientMapper.findClientByMoniker(clientMoniker); + if(client == null){ + throw new InvalidShortIdException(); + } + } + + 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)) { + orderIds = orderIds.trim().replace(",", ","); + params.put("order_ids", orderIds); + String[] orderIdArray = orderIds.split(","); + List orderAmountList = new ArrayList<>(); + for (int i = 0; i < orderIdArray.length; i++) { + JSONObject orderInfo = orderMapper.findOrderById(orderIdArray[i]); + // 判断该笔订单是否存在,是否属于该商户 + if (orderInfo == null) + throw new OrderNotExistsException(); + else { + if (!clientMoniker.equals(orderInfo.getString("partner_code"))) { + throw new OrderNotMatchException(); + } + orderAmountList.add(orderInfo.getString("total_amount")); + } + } + params.put("order_amounts", StringUtils.join(orderAmountList, ",")); + } + riskEventMapper.save(params); + } + + @Override + public void updateRiskEvent(JSONObject params) { + riskEventMapper.update(params); + } + + @Override + public void downloadAuditMaterialZiP(String riskId, HttpServletResponse response) { + + JSONObject riskEvent = riskEventMapper.findById(riskId); + String clientMoniker = riskEvent.getString("client_moniker"); + JSONObject param = new JSONObject(); + param.put("risk_id", riskId); + param.put("orderby_type", "update_time"); + JSONObject riskMaterial = getRiskMaterial(param); + try { + String downloadFilename = clientMoniker + "_audit_materials_" + DateFormatUtils.format(new Date(), "dd/MM/yyyy HH:mm:ss").toString() + ".zip"; + response.setContentType("application/octet-stream"); + response.setHeader("Content-Disposition", "attachment;filename=" + downloadFilename); + ZipOutputStream zos = new ZipOutputStream(response.getOutputStream()); + for(int i=1;i<=6;i++){ + if(riskMaterial.containsKey("file"+i)){ + List fileList= (List)riskMaterial.get("file"+i); + for(String fileUrl : fileList){ + zos.putNextEntry(new ZipEntry("file" + i+fileUrl.substring(fileUrl.lastIndexOf("/")))); + InputStream inputStream = new URL(fileUrl).openConnection().getInputStream(); + byte[] buffer = new byte[1024]; + int result = 0; + while ((result = inputStream.read(buffer)) != -1) { + zos.write(buffer, 0, result); + } + inputStream.close(); + } + } + } + zos.flush(); + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void sendUploadEmail(String riskId) throws IOException { + JSONObject event = getRiskEventDetail(riskId); + Context ctx = getMailContext(event); + final List emailsTos = (List)ctx.getVariable("emailsTos"); + final List emailsCcs = ctx.getVariable("emailsCcs")==null?new ArrayList<>():(List)ctx.getVariable("emailsCcs"); + final String title = (String)ctx.getVariable("title"); + final String content = thymeleaf.process("mail/risk_upload_mail.html", ctx); + final String uploadUrl = (String)ctx.getVariable("uploadUrl"); + royalThreadPoolExecutor.execute(() -> { + try { + String emailId = mailService.sendRiskEmail(title, emailsTos.isEmpty() ? "" : StringUtils.join(emailsTos, ","), + emailsCcs.isEmpty() ? "" : StringUtils.join(emailsCcs, ","), content, event.getIntValue("order_type")==3?(List)ctx.getVariable("files"):null,event.getIntValue("order_type")); + event.put("email_status",1); + 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()) { + event.put("result_type", RiskResultTypeEnum.ALREADY_HANDLED.getResultType()); + } + riskEventMapper.update(event); + } catch (Exception e) { + throw new EmailException("Email Sending Failed", e); + } + }); + 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 tokens = clientDeviceTokenMapper.listTokensByClient_id(client.getIntValue("client_id")); + for (final JSONObject devToken : tokens) { + Runnable task = () -> { + String token = devToken.getString("token"); + if (token == null) { + return; + } + JSONObject log = saveAppMessageLog(devToken.getString("dev_id"), devToken.getIntValue("client_id"), "risk", 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")); + AppMsgSender sender = senderMap.get(devToken.getString("client_type")); + if (token == null || sender == null) { + return; + } + JSONObject managerMsg = new JSONObject(); + managerMsg.put("title", LocaleSupport.localeMessage("app.message.title.risk")); + managerMsg.put("body", + LocaleSupport.localeMessage("app.message.body.risk")); + managerMsg.put("type", type); + managerMsg.put("data", event); + managerMsg.put("msgType", "risk"); + AppMessage appMessage = new AppManagerMessageBuilder(managerMsg).buildMessage(); + sender.sendMessage(appMessage, devToken); + log.put("status", 2); + appMessageLogMapper.update(log); + } 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); + } + }; + sendingAppleMsgPool.execute(task); + } + } + + private JSONObject saveAppMessageLog(String dev_id, int client_id, String messageType, String dev_token, String remark) { + JSONObject log = new JSONObject(); + log.put("dev_id", dev_id); + log.put("client_id", client_id); + log.put("msg_type", messageType); + log.put("dev_token", dev_token); + log.put("remark", remark); + log.put("send_time", new Date()); + appMessageLogMapper.save(log); + return log; + + } + + @Override + public void sendRefuseEmail(String riskId) throws IOException { + JSONObject event = getRiskEventDetail(riskId); + Context ctx = getMailContext(event); + ctx.setVariable("refuse",true); + final List emailsTos = (List)ctx.getVariable("emailsTos"); + final List emailsCcs = ctx.getVariable("emailsCcs")==null?new ArrayList<>():(List)ctx.getVariable("emailsCcs"); + final String uploadUrl = (String)ctx.getVariable("uploadUrl"); + final String content = thymeleaf.process("mail/risk_upload_mail.html", ctx); + royalThreadPoolExecutor.execute(() -> { + try { + String emailId = mailService.sendRiskEmail("You need to resubmit risk materials", emailsTos.isEmpty() ? "" : StringUtils.join(emailsTos, ","), + emailsCcs.isEmpty() ? "" : StringUtils.join(emailsCcs, ","), content, event.getIntValue("order_type")==3?(List)ctx.getVariable("files"):null,event.getIntValue("order_type")); + event.put("email_status",2); + event.put("result_type",RiskResultTypeEnum.MATERIAL_NOT_PASS.getResultType()); + event.put("submit_url",uploadUrl); + riskEventMapper.update(event); + } catch (Exception e) { + throw new EmailException("Email Sending Failed", e); + } + }); + } + + @Override + public void sendUrgeEmail(String riskId) throws IOException { + JSONObject event = getRiskEventDetail(riskId); + Context ctx = getMailContext(event); + final List emailsTos = (List)ctx.getVariable("emailsTos"); + final List emailsCcs = ctx.getVariable("emailsCcs")==null?new ArrayList<>():(List)ctx.getVariable("emailsCcs"); + final String content = thymeleaf.process("mail/risk_urge_mail.html", ctx); + royalThreadPoolExecutor.execute(() -> { + try { + String emailId = mailService.sendRiskEmail("Please submit risk materials as soon as possible", emailsTos.isEmpty() ? "" : StringUtils.join(emailsTos, ","), + emailsCcs.isEmpty() ? "" : StringUtils.join(emailsCcs, ","), content, event.getIntValue("order_type")==3?(List)ctx.getVariable("files"):null,event.getIntValue("order_type")); + event.put("email_status",3); + riskEventMapper.update(event); + } catch (Exception e) { + throw new EmailException("Email Sending Failed", e); + } + }); + } + + private Context getMailContext(JSONObject event) throws IOException { + JSONObject client = clientMapper.findClientByMonikerAll(event.getString("client_moniker")); + if (client == null) { + throw new InvalidShortIdException(); + } + String codeKey = RandomStringUtils.random(20, true, true); + while(stringRedisTemplate.boundValueOps(getRiskUploadKey(codeKey)).get()!=null ){ + codeKey = RandomStringUtils.random(20, true, true); + } + String codeKeyValue = RandomStringUtils.random(10, true, true); + String expireDay = "7"; + if(event.getIntValue("order_type")>2){ + expireDay = "3"; + } + stringRedisTemplate.boundValueOps(getRiskUploadKey(codeKey)).set(codeKeyValue, Long.parseLong(expireDay), TimeUnit.DAYS); + String uploadUrl = PlatformEnvironment.getEnv().concatUrl("/risk/upload/") + event.getString("risk_id") + "/" + codeKey; + List bds = clientBDMapper.listClientBDInfoAvailable(client.getIntValue("client_id"), new Date()); + List bdNames = new ArrayList<>(); + List bdEmails = new ArrayList<>(); + for (JSONObject bd : bds) { + String bdName = bd.getString("display_name"); + if (StringUtils.isNotEmpty(bdName)) { + bdNames.add(bdName); + } + String email = bd.getString("email"); + if (StringUtils.isNotEmpty(email)) { + bdEmails.add(email); + } + } + List clientEmails = new ArrayList<>(); + clientEmails.add(client.getString("contact_email")); + String bdNamesStr = bdNames.isEmpty() ? "" : StringUtils.join(bdNames, ","); + String reply_date = DateFormatUtils.format(DateUtils.addDays(event.getDate("reply_email_date"),-1),"yyyy年MM月dd日"); + String reply_date_english = DateFormatUtils.format(DateUtils.addDays(event.getDate("reply_email_date"),-1),"dd/MM/yyyy"); + + Context ctx = new Context(); + ctx.setVariable("order_type", event.getIntValue("order_type")); + ctx.setVariable("bdNamesStr", bdNamesStr); + ctx.setVariable("reply_date", reply_date); + ctx.setVariable("reply_date_english", reply_date_english); + ctx.setVariable("client", client); + ctx.setVariable("uploadUrl", uploadUrl); + ctx.setVariable("royalpay_order_type", event.getIntValue("royalpay_order_type")); + ctx.setVariable("warning_order_type", event.getIntValue("warning_order_type")); + switch (event.getIntValue("order_type")){ + case 1: + case 2: + String[] orderIds = event.getString("order_ids").split(","); + List orders = new ArrayList(); + for(String orderId : orderIds){ + JSONObject order = orderMapper.findOrderById(orderId); + if(order==null){ + throw new BadRequestException("Order: "+orderId+" not exists"); + } + orders.add(order); + } + ctx.setVariable("orders", orders); + ctx.setVariable("title","Your merchants needs to submit risk materials"); + ctx.setVariable("emailsTos", bdEmails); + break; + case 3: + List attachList = new ArrayList<>(); + JSONObject file = new JSONObject(); + file.put("name", client.getString("short_name")+ "被查单号相关信息.xlsx"); + file.put("content", Base64.encodeBase64String(generateRiskOrders(event))); + attachList.add(file); + ctx.setVariable("files",attachList); + case 4: + ctx.setVariable("title","RoyalPay风控调查 — " + client.getString("short_name")); + ctx.setVariable("emailsCcs", bdEmails); + ctx.setVariable("emailsTos", clientEmails); + break; + } + return ctx; + } + private String getRiskUploadKey(String codeKey){ + return UPLOAD_MAIL_PREFIX + codeKey; + } + + @Override + public JSONObject getRiskMaterial(JSONObject param) { + List riskMaterialList = riskMaterialMapper.findAllMaterials(param.getString("risk_id")); + if (riskMaterialList != null && riskMaterialList.size() > 0){ + List files = riskFileMapper.findAllFiles(riskMaterialList.get(0).getString("material_id")); + JSONObject fileNew = new JSONObject(); + fileNew.put("description",riskMaterialList.get(0).getString("description")); + for(JSONObject file : files){ + int fileType = file.getIntValue("file_type"); + if(!fileNew.containsKey("file"+fileType)){ + List fileList = new ArrayList<>(); + fileList.add(file.getString("file_url")); + fileNew.put("file"+fileType,fileList); + }else{ + List fileList = (List)fileNew.get("file"+fileType); + fileList.add(file.getString("file_url")); + fileNew.put("file"+fileType,fileList); + } + } + return fileNew; + } + + return null; + } + + private byte[] generateRiskOrders(JSONObject event) throws IOException { + String[] orderIds = event.getString("order_ids").split(","); + Workbook wb = new XSSFWorkbook(); + for(String orderId : orderIds){ + 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); + Row row1 = sheet.createRow(1); + Row row2 = sheet.createRow(2); + Row row3 = sheet.createRow(3); + Row row4 = sheet.createRow(4); + Row row5 = sheet.createRow(5); + Row row6 = sheet.createRow(6); + Row row7 = sheet.createRow(7); + Row row8 = sheet.createRow(8); + Row row9 = sheet.createRow(9); + Row row10 = sheet.createRow(10); + Row row11 = sheet.createRow(11); + Row row12 = sheet.createRow(12); + Row row13 = sheet.createRow(13); + Row row14 = sheet.createRow(14); + Row row15 = sheet.createRow(15); + row0.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Partner"); + row0.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getJSONObject("client").getString("short_name")+"("+orderDetail.getJSONObject("client").getString("client_moniker")+")"); + row1.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Order ID"); + row1.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("order_id")); + row2.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Platform Transaction ID"); + row2.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("system_transaction_id")); + row3.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Order Description"); + row3.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(StringUtils.defaultString(orderDetail.getString("order_description"))); + row4.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Customer ID"); + row4.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("customer_id")); + row5.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("IP"); + row5.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("customer_ip")); + row6.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Total Amount"); + row6.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("currency")+" "+orderDetail.getString("total_amount")); + row7.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Input Amount"); + row7.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("currency")+" "+orderDetail.getString("display_amount")); + row8.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Pay Amount"); + row8.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("currency")+" "+orderDetail.getString("customer_payment_amount")); + row9.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Exchange Rate"); + row9.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("exchange_rate")); + row10.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Clearing Amount"); + row10.createCell(1,Cell.CELL_TYPE_STRING).setCellValue("AUD "+ orderDetail.getString("clearing_amount")); + row11.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Gateway"); + row11.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(getGateWay(orderDetail.getIntValue("gateway"))); + row12.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Create Time"); + row12.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("create_time")); + row13.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Status"); + row13.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(getStatus(orderDetail.getIntValue("status"))); + row14.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Pay Time"); + row14.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(orderDetail.getString("transaction_time")); + row15.createCell(0,Cell.CELL_TYPE_STRING).setCellValue("Order Detail"); + row15.createCell(1,Cell.CELL_TYPE_STRING).setCellValue(StringUtils.defaultString(orderDetail.getString("order_detail"))); + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + wb.write(bos); + bos.flush(); + return bos.toByteArray(); + } + private String getGateWay(int gateWay){ + switch (gateWay) { + case 0: + return "线下扫码"; + case 1: + return "线下扫码"; + case 2: + return "商户静态码"; + case 3: + return "线上网关"; + case 4: + return "JSAPI网关"; + case 5: + return "线下网关"; + case 6: + return "线下网关"; + case 7: + return "商户静态码"; + case 8: + return "Mobile H5"; + case 9: + return "第三方网关"; + case 10: + return "APP网关"; + case 11: + return "账单码"; + case 12: + return "小程序"; + case 13: + return "原生二维码"; + case 14: + return "账单链接"; + } + return ""; + } + + private String getStatus(int status){ + switch (status) { + case 0: + return "Creating"; + case 1: + return "Failed Create Order"; + case 2: + return "Wait For Payment"; + case 3: + return "Closed"; + case 4: + return "Payment Failed"; + case 5: + return "Payment Success"; + case 6: + return "Partial Refund"; + case 7: + return "Full Refund"; + } + return ""; + } + + @Override + public void checkTodo(JSONObject manager, List notices) { + if (ManagerRole.BD_USER.hasRole(manager.getIntValue("role"))) { + JSONObject params = new JSONObject(); + params.put("bd_id", manager.getString("manager_id")); + params.put("date", new Date()); + List riskClientList = clientBDMapper.listBDClientInfo(params); + boolean noticeFlag = false; + for (JSONObject client : riskClientList) { + if (!noticeFlag) { + params.put("client_moniker", client.getString("client_moniker")); + List riskEventList = riskEventMapper.findAll(params); + for (JSONObject event : riskEventList) { + Integer resultType = event.getIntValue("result_type"); + Integer orderType = event.getIntValue("order_type"); + // Integer是对象,所以用equals方法比较 + if ((resultType.equals(RiskResultTypeEnum.SEND_EMAIL_TO_BD.getResultType()) || resultType.equals(RiskResultTypeEnum.MATERIAL_NOT_PASS.getResultType())) && (orderType.equals(RiskOrderTypeEnum.WECHAT_ORDER.getOrderType()) || orderType.equals(RiskOrderTypeEnum.ALIPAY_ORDER.getOrderType()))) { + noticeFlag = true; + break; + } + } + } else { + break; + } + } + if (noticeFlag) { + String msg = LocaleSupport.localeMessage("todo.bd.order"); + notices.add(new TodoNotice("riskBusiness", msg, "#/analysis/monitoring/risk_business_bd")); + } + } + } } diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskUploadServiceIpml.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskUploadServiceIpml.java new file mode 100644 index 000000000..17133ae4a --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/core/impl/RiskUploadServiceIpml.java @@ -0,0 +1,90 @@ +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; +import java.util.Date; +import java.util.List; + +@Service +public class RiskUploadServiceIpml implements RiskUploadService { + @Resource + private RiskMaterialMapper riskMaterialMapper; + @Resource + private RiskFileMapper riskFileMapper; + @Resource + private RiskEventMapper riskEventMapper; + @Resource + private StringRedisTemplate stringRedisTemplate; + private final String UPLOAD_MAIL_PREFIX = "UPLOAD_MAIL"; + + @Override + public void submitMaterial(JSONObject material) { + JSONObject event = riskEventMapper.findById(material.getString("risk_id")); + 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++){ + if(material.containsKey("file"+i+"_url")){ + List urls = (List)material.get("file"+i+"_url"); + for(String url:urls){ + JSONObject file = new JSONObject(); + file.put("file_url",url); + file.put("file_type",i); + file.put("material_id",material.getString("material_id")); + riskFileMapper.save(file); + } + } + } + event.put("result_type", RiskResultTypeEnum.WAIT_FOR_AUDIT.getResultType()); + riskEventMapper.update(event); + } + + @Override + public void deleteUploadMailKey(String codeKey) { + stringRedisTemplate.delete(getRiskUploadKey(codeKey)); + } + + @Override + public void checkUploadMailKey(String codeKey,String risk_id) { + JSONObject event = riskEventMapper.findById(risk_id); + //到期日前一天的下午6点前url可用 + try { + String reply = DateFormatUtils.format(DateUtils.addDays(event.getDate("reply_email_date"),-1),"yyyy-MM-dd 18:00:00"); + if(new Date().after( DateUtils.parseDate(reply,new String[]{"yyyy-MM-dd HH:mm:ss"}))){ + deleteUploadMailKey(codeKey); + throw new BadRequestException("Url expired"); + } + } catch (ParseException e) { + e.printStackTrace(); + } + + if (StringUtils.isNotEmpty(codeKey)) { + String redisUpload = stringRedisTemplate.boundValueOps(getRiskUploadKey(codeKey)).get(); + if (redisUpload == null) { + throw new BadRequestException("Url expired"); + } + } + } + private String getRiskUploadKey(String codeKey){ + return UPLOAD_MAIL_PREFIX + codeKey; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskEmailStatusEnum.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskEmailStatusEnum.java new file mode 100644 index 000000000..7f9f2e570 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskEmailStatusEnum.java @@ -0,0 +1,27 @@ +package au.com.royalpay.payment.manage.riskbusiness.enums; + +/** + * @Author lvjian + * @Date 2018/10/16 22:22 + */ +public enum RiskEmailStatusEnum { + NOT_SEND(0), + ALREADY_SEND(1), + BACK_AND_SEND(2), + SEND_EMAIL_AGAIN(3), + ; + + private Integer emailStatus; + + RiskEmailStatusEnum(Integer emailStatus) { + this.emailStatus = emailStatus; + } + + public Integer getEmailStatus() { + return emailStatus; + } + + public void setEmailStatus(Integer emailStatus) { + this.emailStatus = emailStatus; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskOrderTypeEnum.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskOrderTypeEnum.java new file mode 100644 index 000000000..d0d5604ff --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskOrderTypeEnum.java @@ -0,0 +1,29 @@ +package au.com.royalpay.payment.manage.riskbusiness.enums; + +/** + * @Author lvjian + * @Date 2018/10/16 20:18 + */ +public enum RiskOrderTypeEnum { + + WECHAT_ORDER(1), + ALIPAY_ORDER(2), + ROYALPAY_ORDER(3), + WARNING_ORDER(4), + GENERAL_ORDER(5) + ; + + private Integer orderType; + + public Integer getOrderType() { + return orderType; + } + + public void setOrderType(Integer orderType) { + this.orderType = orderType; + } + + RiskOrderTypeEnum(Integer orderType) { + this.orderType = orderType; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskResultTypeEnum.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskResultTypeEnum.java new file mode 100644 index 000000000..2938586d8 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/enums/RiskResultTypeEnum.java @@ -0,0 +1,29 @@ +package au.com.royalpay.payment.manage.riskbusiness.enums; + +/** + * @Author lvjian + * @Date 2018/10/16 20:40 + */ +public enum RiskResultTypeEnum { + + NOT_HANDLED(0), + SEND_EMAIL_TO_BD(1), + WAIT_FOR_AUDIT(2), + MATERIAL_AUDIT_PASS(3), + MATERIAL_NOT_PASS(4), + ALREADY_HANDLED(5); + + private Integer resultType; + + RiskResultTypeEnum(Integer resultType) { + this.resultType = resultType; + } + + public Integer getResultType() { + return resultType; + } + + public void setResultType(Integer resultType) { + this.resultType = resultType; + } +} diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java index 3b2d5f08e..ba33b0ab9 100644 --- a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskBusinessController.java @@ -1,28 +1,132 @@ package au.com.royalpay.payment.manage.riskbusiness.web; +import au.com.royalpay.payment.manage.merchants.beans.PartnerQuery; +import au.com.royalpay.payment.manage.merchants.core.ClientManager; import au.com.royalpay.payment.manage.permission.manager.ManagerMapping; +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.enums.RiskResultTypeEnum; +import au.com.royalpay.payment.tools.CommonConsts; import au.com.royalpay.payment.tools.permission.enums.ManagerRole; import com.alibaba.fastjson.JSONObject; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import com.sun.org.apache.xerces.internal.impl.dv.xs.BooleanDV; +import com.sun.org.apache.xpath.internal.operations.Bool; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; /** + * 风控业务 * @Author lvjian * @Date 2018/10/10 1:12 */ @RestController -@RequestMapping("/risk/business/") +@ManagerMapping(value = "/risk/business/", role = {ManagerRole.OPERATOR, ManagerRole.ADMIN, ManagerRole.BD_USER, ManagerRole.RISK_MANAGER}) public class RiskBusinessController { - @ManagerMapping(value = "event", method = RequestMethod.GET, role = {ManagerRole.OPERATOR, ManagerRole.ADMIN}) - public String testHello() { - return "Hello"; + @Autowired + private RiskBusinessService riskBusinessService; + + @Autowired + private ClientManager clientManager; + + @GetMapping(value = "events") + public JSONObject getRiskEvents(RiskEventQuery riskEventQuery, @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) { + JSONObject params = riskEventQuery.toJSON(); + return riskBusinessService.getRiskEventsByPage(params, manager); + } + + @GetMapping(value = "events/{risk_id}") + public JSONObject getRiskEventDetail(@PathVariable("risk_id") String riskId, + @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) { + JSONObject riskEvent = riskBusinessService.getRiskEventDetail(riskId); + List tradeLogs = riskBusinessService.getRiskEventOrderList(riskEvent.getString("order_ids")); + riskEvent.put("tradeLogs", tradeLogs); + return riskEvent; + } + + @PostMapping(value = "events") + public void RegisterRiskEvent(@RequestBody JSONObject params, + @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) { + riskBusinessService.addRiskEvent(params, manager); + } + + @PutMapping(value = "events") + public void UpdateRiskEvent(@RequestBody JSONObject params) { + riskBusinessService.updateRiskEvent(params); + } + + @GetMapping(value = "/{risk_id}/download/materialsAsZIP") + public void downloadComplianceZip(@PathVariable("risk_id") String riskId, HttpServletResponse response) throws Exception { + riskBusinessService.downloadAuditMaterialZiP(riskId, response); + } + + + @RequestMapping(value = "/{risk_id}/upload_mail",method = RequestMethod.PUT) + public void uploadEmail(@PathVariable String risk_id) throws IOException { + riskBusinessService.sendUploadEmail(risk_id); + } + + @RequestMapping(value = "/{risk_id}/refuse",method = RequestMethod.PUT) + public void refuseEmail(@PathVariable String risk_id) throws IOException { + riskBusinessService.sendRefuseEmail(risk_id); + } + + @GetMapping(value = "/{risk_id}/material") + public JSONObject getRiskMaterial(@PathVariable("risk_id") String riskId) { + JSONObject param = new JSONObject(); + param.put("risk_id", riskId); + return riskBusinessService.getRiskMaterial(param); + } + + @PutMapping(value = "/channel/{channel}/permission/{channelFlag}") + public void updateMerchantChannel(@RequestBody JSONObject params, + @PathVariable("channelFlag") Boolean channelFlag, + @PathVariable("channel") String channel, + @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) { + clientManager.switchChannelPermission(manager, params.getString("client_moniker"), channel, channelFlag); + + if (channelFlag) + params.put("result_type", RiskResultTypeEnum.ALREADY_HANDLED.getResultType()); + riskBusinessService.updateRiskEvent(params); + } + + @PutMapping(value = "/partner/{isValid}") + public void updateMerchantValid(@RequestBody JSONObject params, + @PathVariable("isValid") Boolean isValid, + @ModelAttribute(CommonConsts.MANAGER_STATUS) JSONObject manager) { + String clientMoniker = params.getString("client_moniker"); + if (isValid) { + clientManager.disableClient(clientMoniker, manager); + Integer temporaryCloseMerchant = params.getInteger("temporary_close_merchant"); + if (temporaryCloseMerchant != 1) { + params.put("result_type", RiskResultTypeEnum.ALREADY_HANDLED.getResultType()); + } + } + else { + clientManager.revertClient(clientMoniker, manager); + params.put("result_type", RiskResultTypeEnum.ALREADY_HANDLED.getResultType()); + } + riskBusinessService.updateRiskEvent(params); + + } + @RequestMapping(value = "/{risk_id}/urge",method = RequestMethod.PUT) + public void urgeEmail(@PathVariable String risk_id) throws IOException { + riskBusinessService.sendUrgeEmail(risk_id); } - @ManagerMapping(value = "event/{risk_id}", method = RequestMethod.GET, role = {ManagerRole.OPERATOR, ManagerRole.ADMIN}) - public JSONObject getRiskEventDetail() { - return null; + @GetMapping(value = "/partners") + public List getPartners(PartnerQuery partnerQuery) { + JSONObject param = partnerQuery.toJsonParam(); + return clientManager.getClientBySimpleQuery(param); } } diff --git a/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskFileUploadController.java b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskFileUploadController.java new file mode 100644 index 000000000..68bb91911 --- /dev/null +++ b/src/main/java/au/com/royalpay/payment/manage/riskbusiness/web/RiskFileUploadController.java @@ -0,0 +1,50 @@ +package au.com.royalpay.payment.manage.riskbusiness.web; + +import au.com.royalpay.payment.manage.riskbusiness.core.RiskBusinessService; +import au.com.royalpay.payment.manage.riskbusiness.core.RiskUploadService; +import com.alibaba.fastjson.JSONObject; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("/risk/upload") +public class RiskFileUploadController { + @Resource + private RiskUploadService riskUploadService; + @Resource + private RiskBusinessService riskBusinessService; + + /** + * 上传材料的链接 + * @param codeKey + * @param risk_id + * @return + */ + @RequestMapping(value = "/{risk_id}/{codeKey}", method = RequestMethod.GET) + public ModelAndView jumpVerifyMail(@PathVariable String codeKey, @PathVariable String risk_id) { + //检查codekey是否有效 + riskUploadService.checkUploadMailKey(codeKey,risk_id); + JSONObject event = riskBusinessService.getRiskEventDetail(risk_id); + ModelAndView view = new ModelAndView("mail/risk_upload"); + view.addObject("codeKey", codeKey); + view.addObject("risk_id",risk_id); + view.addObject("order_type",event.getIntValue("order_type")); + view.addObject("short_name",event.getJSONObject("clientInfo").getString("short_name")); + return view; + } + + /** + * 上传调单材料 + * @param codeKey + * @param material + */ + @RequestMapping(value = "/{codeKey}", method = RequestMethod.POST) + public void upload(@PathVariable String codeKey, @RequestBody JSONObject material) { + riskUploadService.checkUploadMailKey(codeKey, material.getString("risk_id")); + riskUploadService.submitMaterial(material); + riskUploadService.deleteUploadMailKey(codeKey); + } + +} diff --git a/src/main/java/au/com/royalpay/payment/manage/support/attachment/web/AttachmentController.java b/src/main/java/au/com/royalpay/payment/manage/support/attachment/web/AttachmentController.java index 34a8f2907..bccc665f9 100644 --- a/src/main/java/au/com/royalpay/payment/manage/support/attachment/web/AttachmentController.java +++ b/src/main/java/au/com/royalpay/payment/manage/support/attachment/web/AttachmentController.java @@ -27,6 +27,10 @@ public class AttachmentController { public JSONObject uploadImage(@RequestParam MultipartFile file) throws IOException { return attachmentClient.uploadFile(file,false); } + @RequestMapping(value = "/riskFiles", method = RequestMethod.POST) + public JSONObject uploadRiskImage(@RequestParam MultipartFile file) throws IOException { + return attachmentClient.uploadFile(file,false); + } @RequestMapping(value = "/secret_files", method = RequestMethod.POST) @RequirePartner diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index 91173a856..51ea1ca57 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -1,6 +1,6 @@ spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.schema-name=royalpay_production -spring.datasource.host=192.168.0.49:3306 +spring.datasource.host=192.168.0.111:3306 spring.datasource.url=jdbc:mysql://${spring.datasource.host}/${spring.datasource.schema-name}?useUnicode=true&characterEncoding=utf8&useSSL=false spring.datasource.username=root spring.datasource.password=root \ No newline at end of file diff --git a/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/OrderMapper.xml b/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/OrderMapper.xml index de0da503a..c9eda37b7 100644 --- a/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/OrderMapper.xml +++ b/src/main/resources/au/com/royalpay/payment/manage/mappers/payment/OrderMapper.xml @@ -738,6 +738,81 @@ and t.clearing_status=#{clearing_status} + + + SELECT + re.risk_id, + re.client_moniker, + re.order_ids, + re.order_type, + re.royalpay_order_type, + re.warning_order_type, + re.description, + re.email_status, + re.receive_email_date, + re.reply_email_date, + re.fillin_person, + re.fillin_id, + re.result_type, + re.channel_result, + re.temporary_close_channel, + re.temporary_close_merchant, + re.in_merchant_blacklist, + re.in_user_blacklist, + re.processed_remark, + re.create_time, + sc.industry, + sc.short_name, + IFNULL(sc.sub_merchant_id, re.sub_merchant_id) sub_merchant_id + FROM risk_event re + LEFT JOIN sys_clients sc + ON re.client_moniker = sc.client_moniker + + RIGHT JOIN( + SELECT distinct re.risk_id + FROM + risk_event re, + risk_event_help reh + + reh.risk_event_help_id < (LENGTH(re.order_amounts) - LENGTH(REPLACE(re.order_amounts, ',' , ''))) + 1 + + AND cast(SUBSTRING_INDEX(SUBSTRING_INDEX(order_amounts,',',reh.risk_event_help_id + 1),',',-1) as signed) >= #{start_amount} + + + + AND cast(SUBSTRING_INDEX(SUBSTRING_INDEX(order_amounts,',',reh.risk_event_help_id + 1),',',-1) as signed) <= #{end_amount} + + + + ) temp + on re.risk_id = temp.risk_id + + + + LEFT JOIN( + SELECT DISTINCT client_id + FROM sys_client_bd + WHERE + is_valid = 1 + AND bd_id = #{bd_id} + AND end_date > NOW() + ) scb + ON sc.client_id = scb.client_id + + + + AND re.risk_id = #{risk_id} + + + AND re.client_moniker = #{client_moniker} + + + + + #{order_type_item} + + + + + AND re.order_type = #{order_type} + + + + + #{result_type_item} + + + + + AND re.result_type = #{result_type} + + + + AND re.order_ids LIKE CONCAT('%', #{order_ids}, '%') + + + AND sc.sub_merchant_id = #{sub_merchant_id} + + + AND sc.industry = #{industry} + + + AND re.receive_email_date = #{receive_email_date} + + + AND re.reply_email_date = #{reply_email_date} + + + + + \ No newline at end of file diff --git a/src/main/resources/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.xml b/src/main/resources/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.xml new file mode 100644 index 000000000..5db6ff17c --- /dev/null +++ b/src/main/resources/au/com/royalpay/payment/manage/mappers/riskbusiness/RiskMaterialMapper.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/src/main/resources/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.xml b/src/main/resources/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.xml index d525812a4..a80c8423e 100644 --- a/src/main/resources/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.xml +++ b/src/main/resources/au/com/royalpay/payment/manage/mappers/system/ClientBDMapper.xml @@ -41,11 +41,29 @@ #{date} OR end_date IS NULL) AND - b.is_valid = 1 + INNER JOIN sys_client_bd b + ON b.bd_id = m.manager_id + WHERE client_id = #{client_id} + AND start_date <= #{date} + AND (end_date > #{date} OR end_date IS NULL) + AND b.is_valid = 1 + ]]> + + + + + + + + + + + + + + + +
+ Submit +
+ + + + + + + + + + + +
+
+ + diff --git a/src/main/resources/templates/mail/risk_upload_mail.html b/src/main/resources/templates/mail/risk_upload_mail.html new file mode 100644 index 000000000..16459d5c4 --- /dev/null +++ b/src/main/resources/templates/mail/risk_upload_mail.html @@ -0,0 +1,94 @@ + +
+ Dear : +

您好,您提交的风控材料已被拒绝。请于下午6:00(悉尼时间)前提供被查商户 ()的以下材料

+

请提供以下被查单号的小票, 物流单据(如有邮寄产品的情况), 以及消费者与买家的聊天记录等来佐证被查交易单号。 被查交易单号如下:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Order IDAmountInput AmountAUD AmountExchange RateStatusCreate TimeGateway
+

如果提交的材料不齐, 则有关停支付的风险。还请按时提供商户材料并直接回复该邮件, 感谢,辛苦。

+ +

Submit Risk Materials

+
+
+ 尊敬的RoyalPay商户 :
+ Dear RoyalPay merchant :
+

您提交的风控材料已被拒绝。
+ The risk materials you submitted has been rejected +

+

近期由于我们的风控系统检测到您的交易异常,已暂时将您的清算周期调整为T+,还请您提供以下材料,还原附件中列明的交易的真实背景:
+ RoyalPay's risk management system recently has identified abnormal transactions from your records, clean days has been adjusted to T+.so please provide us with following materials to assist in verifying the real scenario of the transactions attached:

+

1.请解释相应的消费场景/业务模式,例如网站商城,扫码支付, 消费者到店支付等;
+   Please explain the relative payment scenario/business activities, for example, online store, QR code payment, payment at the store, etc;

+

2.提供相应购物清单,订单小票(请提供与被查交易订单号相匹配的交易时间及金额的发票);
  +   Provide related shopping lists, invoices. (Please provide the invoices, amount of which matches that of the abnormal transaction);

+

3.提供相应的发货证明,报关单(若有消费者在国内购买,请提供物流单据或报关单);
+   Relative proof of delivery, customs declaration (If the consumer purchased from China, please provide shipping receipt or customs declaration);

+

4.提供您的门店照片(门店照及店铺内的照片各一张, 一张可以看到商户名的门头照,一张可以看到相关商品或服务的店内照片);
+     Photos of the store ( one of each front-store and in-store);

+

5.其他可以还原交易背景的资料,如和消费者的聊天记录等,来佐证被查单号交易的真实性;
+   Other materials that can verify the payment scenario, for example, chatting history, to prove the truth of the transactions;

+

注:以上证件需原件扫描件/数码拍摄件,且照片内容需真实有效,不得做任何修改。

+

请查收附件中关于被抽查的订单交易的相关信息,并在下午6:00 (悉尼时间)前将所需材料直接回复该邮件,未能按时提交完整证明材料,支付渠道将被关停,请您务必配合调查。感谢。
+ Please find sampled transactions in attachment, and reply required materials to this email before 6:00 pm (AEST). If you can not provide qualified materials on time, the payment channels would be suspended or restricted with amount limit. Please be sure to assist the investigation. Thanks.

+ + + +

Submit Risk Materials

+
+
+ 尊敬的RoyalPay商户 :
+ Dear RoyalPay merchant :
+

我司风控系统检测到您短期内后台交易存在异常行为(单人多次大金额交易退款频繁),触发平台风控预警,特此提醒:请勿使用平台进行违规交易,一经核查将关闭支付权限。请知悉。
+ RoyalPay's risk management system has identified abnormal transactions from your records in a short time(Single person pays large sums several timesfrequent refund transactions),which triggered the platform risk control warning. Here reminds: Do not use the platform for illegal transactions. Once verified, the payment authority will be closed.

+
+

顺颂商祺
+ Sincerely +

+

+ RoyalPay 风控团队
+ RoyalPay Risk Management Team +

+

+
+ Contact Us
+ Email:
+ info@royalpay.com.au
+ Tel:
+ 1300 10 77 50
+
+ Service WeChat Account:
+
+ Level 14, 383 Kent Street, Sydney NSW 2000
+
+ Level 11, 15 William Street, Melbourne VIC 3000 +

+

Tunnel Show Pty Ltd trading as RoyalPay
+ Representative of AFSL licensee 448066 +

+ diff --git a/src/main/resources/templates/mail/risk_urge_mail.html b/src/main/resources/templates/mail/risk_urge_mail.html new file mode 100644 index 000000000..00d0b1fab --- /dev/null +++ b/src/main/resources/templates/mail/risk_urge_mail.html @@ -0,0 +1,25 @@ + +Dear RoyalPay Merchant/BD : +

您好,您的风控材料链接即将过期,请及时提交风控材料。未能按时提交完整证明材料,支付渠道将被关停,请您务必配合调查。感谢。

+

Your link to the risk materials is about to expire, please submit the risk materials in time.If you can not provide qualified materials on time, the payment channels would be suspended or restricted with amount limit. Please be sure to assist the investigation. Thanks.

+

Best Regards

+

+
+ Contact Us
+ Email:
+ info@royalpay.com.au
+ Tel:
+ 1300 10 77 50
+
+ Service WeChat Account:
+
+ Level 14, 383 Kent Street, Sydney NSW 2000
+
+ Level 11, 15 William Street, Melbourne VIC 3000 +

+

Tunnel Show Pty Ltd trading as RoyalPay
+ Representative of AFSL licensee 448066 +

+ diff --git a/src/main/ui/risk_upload_success.html b/src/main/ui/risk_upload_success.html new file mode 100644 index 000000000..c596999f8 --- /dev/null +++ b/src/main/ui/risk_upload_success.html @@ -0,0 +1,190 @@ + + + + + + + + Risk Materials | Success + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ + + + +
+ +

We have received your materials, our risk manager will review soon,

+

please wait for result.

+
+ +
+
+
+ +
+
+
+ +
+
+ + diff --git a/src/main/ui/static/analysis/monitoring/templates/analysis_monitoring.html b/src/main/ui/static/analysis/monitoring/templates/analysis_monitoring.html index 0f77b16ab..857ec674a 100644 --- a/src/main/ui/static/analysis/monitoring/templates/analysis_monitoring.html +++ b/src/main/ui/static/analysis/monitoring/templates/analysis_monitoring.html @@ -24,8 +24,14 @@ 风控记录|Risk Records -
  • - 风控业务 + +
  • + 风控业务|Risk Business +
  • + + +
  • + 风控业务|Risk Business
  • diff --git a/src/main/ui/static/analysis/risk_business.js b/src/main/ui/static/analysis/risk_business.js index 808a4bccb..b3f5435d5 100644 --- a/src/main/ui/static/analysis/risk_business.js +++ b/src/main/ui/static/analysis/risk_business.js @@ -4,29 +4,599 @@ define(['angular', 'jquery', 'uiRouter', './monitoring/analysis-monitoring'], function (angular, $) { 'use strict'; + var orderTypesMap = { + "1": "微信调单", + "2": "支付宝调单", + "3": "RoyalPay调单", + "4": "警告", + "5": "通用号调单" + }; + + var orderTypesMapForBD = { + "1": "微信调单", + "2": "支付宝调单" + }; + + var royalpayOrderTypesMap = { + "0": "正常调单", + "1": "单纯大金额频繁刷单" + }; + + var warningOrderTypesMap = { + "0": "单人多次大金额交易", + "1": "退款频繁" + }; + + var resultTypesMap = { + "0": "未处理", + "1": "已发送邮件", + "2": "已提交材料,等待审核", + "3": "材料审核通过", + "4": "材料审核不通过,已打回", + "5": "已处理" + }; + + var resultTypeSearchMap = { + "0": "未处理", + "1": "资料完善中", + "2": "等待风控", + "3": "材料通过", + "4": "材料打回", + "5": "已处理" + }; + + var resultTypeSearchMapForBD = { + "1": "材料待上传", + "2": "材料已提交", + "3": "材料通过", + "4": "材料打回", + "5": "已处理" + }; + + var emailStatusMap = { + "0": "未发送", + "1": "已发送", + "2": "打回并已发送", + "3": "已发送催促邮件" + }; + + var amountSectionMap = { + "0-500": "0-500", + "500-1000": "500-1000", + "1000-1500": "1000-1500", + "1500-2000": "1500-2000", + "2000-2500": "2000-2500", + "2500-3000": "2500-3000", + "3000-3500": "3000-3500", + "3500-4000": "3500-4000", + "4000-4500": "4000-4500" + }; + var app = angular.module('riskBusinessApp', ['ui.router']); app.config(['$stateProvider', function ($stateProvider) { $stateProvider.state('analysis_monitoring.risk_business', { url: '/risk_business', templateUrl: '/static/analysis/templates/risk_business.html', + cache: false, + controller: 'riskBusinessCtrl' + }).state('analysis_monitoring.risk_business_bd', { + url: '/risk_business_bd', + templateUrl: '/static/analysis/templates/risk_business_bd.html', + cache: false, controller: 'riskBusinessCtrl' - }).state('analysis_monitoring.risk_business.detail', { + }).state('analysis_monitoring.riskEvent_detail', { url: '/{risk_id}/detail', templateUrl: '/static/analysis/templates/riskEvent_detail.html', controller: 'riskEventDetailCtrl', resolve: { - partner: ['$http', '$stateParams', function ($http, $stateParams) { - return $http.get('/risk/business/event' + $stateParams.risk_id); + riskEvent: ['$http', '$stateParams', function ($http, $stateParams) { + return $http.get('/risk/business/events/' + $stateParams.risk_id); }] } - }) + }).state('analysis_monitoring.riskEvent_detail_bd', { + url: '/{risk_id}/bd/detail', + templateUrl: '/static/analysis/templates/riskEvent_detail_bd.html', + controller: 'riskEventDetailCtrl', + resolve: { + riskEvent: ['$http', '$stateParams', function ($http, $stateParams) { + return $http.get('/risk/business/events/' + $stateParams.risk_id); + }] + } + }).state('analysis_monitoring.risk_business.new_riskEvent', { + url: '/new_riskEvent', + templateUrl: '/static/analysis/templates/new_riskEvent.html', + controller: 'newRiskEventCtrl' + }).state('analysis_monitoring.riskEvent_detail.audit_material', { + url: '/audit_material', + templateUrl: '/static/analysis/templates/audit_material.html', + controller: 'auditMaterialCtrl' + }); }]); - app.controller('riskBusinessCtrl', ['$scope', '$http','$filter','chartParser', function ($scope, $http,$filter,chartParser) { + app.controller('riskBusinessCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'commonDialog', 'industryMap', + function ($scope, $state, $http, $uibModal, $filter, commonDialog, industryMap) { + $scope.orderTypes = orderTypesMap; + $scope.orderTypesForBD = orderTypesMapForBD; + $scope.resultTypes = resultTypesMap; + $scope.resultSearchTypes = resultTypeSearchMap; + $scope.resultTypesForBD = resultTypeSearchMapForBD; + $scope.industries = industryMap.configs(); + $scope.amountSection = amountSectionMap; + $scope.pagination = {}; + $scope.params = {}; - }]); + // 加载风险注册事件 + $scope.loadRiskEvents = function (page) { + var params = angular.copy($scope.params); + params.page = page || $scope.pagination.page || 1; + params.replyEmailDate = $filter('date')(params.replyEmailDate, 'yyyy-MM-dd'); + params.receiveEmailDate = $filter('date')(params.receiveEmailDate, 'yyyy-MM-dd'); + if (params.section != null) { + var sectionArray = params.section.split('-'); + params.startAmount = sectionArray[0]; + params.endAmount = sectionArray[1]; + } + $http.get('/risk/business/events', {params: params}).then(function (resp) { + $scope.riskEvents = resp.data.data; + $scope.pagination = resp.data.pagination; + }); + }; + + $scope.loadRiskEvents(1); + } + ]); + + app.controller('riskEventDetailCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'Upload', 'commonDialog', 'riskEvent', + function ($scope, $state, $http, $uibModal, $filter, Upload, commonDialog, riskEvent) { + + $scope.orderTypes = orderTypesMap; + $scope.resultTypes = resultTypesMap; + $scope.riskEvent = riskEvent.data; + // 获取数据库中对应的渠道字段 + var orderChannel = 'enable_'; + if ($scope.riskEvent.order_type == 1) { + orderChannel += 'wechat'; + } else if ($scope.riskEvent.order_type == 2) { + orderChannel += 'alipay'; + } else { + orderChannel = null; + } + // 编辑表格的数据保存对象,重新从源数据复制,从而取消保存操作时不会更新视图 + $scope.riskEventEdit = angular.copy(riskEvent.data); + + // order_type转换为string类型是因为前端select控件这样才会显示初值 + $scope.riskEventEdit.order_type += ""; + + // 调单信息 + $scope.tradeLogs = $scope.riskEvent.tradeLogs; + + // 商户信息 + $scope.clientInfo = $scope.riskEvent.clientInfo; + if (orderChannel != null) + $scope.enableChannel = $scope.clientInfo[orderChannel]; + + // 控制编辑表格的显示 + $scope.editFlag = false; + $scope.changeEditFlag = function(editFlag) { + $scope.editFlag = !editFlag; + // 如果是在编辑状态,需要将日期转换为date类型(前端控件需要) + // 如果是在非编辑状态,需要将日期转换为yyyy-MM-dd格式 + if ($scope.editFlag) { + $scope.riskEventEdit.receive_email_date = new Date($scope.riskEventEdit.receive_email_date.replace(/-/g, "/")); + $scope.riskEventEdit.reply_email_date = new Date($scope.riskEventEdit.reply_email_date.replace(/-/g, "/")); + } else { + $scope.riskEvent.receive_email_date = $filter('date')($scope.riskEvent.receive_email_date, 'yyyy-MM-dd'); + $scope.riskEvent.reply_email_date = $filter('date')($scope.riskEvent.reply_email_date, 'yyyy-MM-dd'); + } + }; + + $scope.save = function(form) { + if (form.$invalid) { + angular.forEach(form, function (item, key) { + if (key.indexOf('$') < 0) { + item.$dirty = true; + } + }); + return; + } + + // 保存时需要重新将日期转换为yyyy-MM-dd格式传入后端 + $scope.riskEventEdit.reply_email_date = $filter('date')($scope.riskEventEdit.reply_email_date, 'yyyy-MM-dd'); + $scope.riskEventEdit.receive_email_date = $filter('date')($scope.riskEventEdit.receive_email_date, 'yyyy-MM-dd'); + $http.put('/risk/business/events', $scope.riskEventEdit).then(function (resp) { + commonDialog.alert({ + title: 'Success', + content: 'Update riskEvent successfully', + type: 'success' + }); + $state.reload('analysis_monitoring.riskEvent_detail'); + }, function (resp) { + commonDialog.alert({ + title: 'Error', + content: resp.data.message, + type: 'error' + }); + }); + }; + + /** + * 关停渠道 + * @param orderType 调单类型 + * @param channelFlag + * @param temporaryFlag 是否临时关停 + */ + $scope.updateChannel = function (orderType, channelFlag, temporaryFlag) { + var channel; + if (orderType == "1") + channel = 'wechat'; + else if (orderType == "2") + channel = 'alipay'; + $scope.riskEvent.temporary_close_channel = temporaryFlag; + commonDialog.confirm({ + title: 'Warning', + content: 'Are you sure?' + }).then(function () { + $http.put('/risk/business/channel/' + channel + '/permission/' + channelFlag, $scope.riskEvent).then(function () { + $state.reload('analysis_monitoring.riskEvent_detail'); + }, function (resp) { + commonDialog.alert({ + title: 'Failed to change ' + channel + ' channel permission status', + content: resp.data.message, + type: 'error' + }) + }); + }) + }; + + // 关停商户 + $scope.updateClient = function(isValid, temporaryFlag) { + $scope.riskEvent.temporary_close_merchant = temporaryFlag; + commonDialog.confirm({ + title: 'Warning', + content: 'Are you sure?' + }).then(function () { + $http.put('/risk/business/partner/' + isValid, $scope.riskEvent).then(function () { + $state.reload('analysis_monitoring.riskEvent_detail'); + }, function (resp) { + commonDialog.alert({title: 'Error', content: resp.data.message, type: 'error'}); + }) + }) + }; + + $scope.resendUploadEmail = function () { + commonDialog.confirm({ + title: 'Warning', + content: 'Please confirm sending mail.' + }).then(function () { + var url = '/risk/business/' + $scope.riskEvent.risk_id + '/upload_mail'; + if ($scope.riskEvent.result_type == 1) + url = '/risk/business/' + $scope.riskEvent.risk_id + '/urge'; + $http.put(url).then(function () { + $state.reload(); + }, function (resp) { + commonDialog.alert({title: 'Error', content: resp.data.message, type: 'error'}); + }) + }) + }; + + + // 以下为BD上传材料相关 + $scope.material={}; + $scope.material.update_time=$filter('date')(new Date(), 'yyyy-MM-dd HH:mm:ss'); + $scope.material.risk_id = $scope.riskEvent.risk_id; + $scope.uploadFile1 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file1Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file1Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file1Progress.value = value/(files.length*2); + }) + } + $scope.material.file1_url = urls; + } + }; + $scope.uploadFile2 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file2Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file2Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file2Progress.value = value/(files.length*2); + }) + } + $scope.material.file2_url = urls; + } + }; + $scope.uploadFile3 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file3Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file3Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file3Progress.value = value/(files.length*2); + }) + } + $scope.material.file3_url = urls; + } + }; + $scope.uploadFile4 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file4Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file4Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file4Progress.value = value/(files.length*2); + }) + } + $scope.material.file4_url = urls; + } + }; + $scope.uploadFile5 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file5Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file5Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file5Progress.value = value/(files.length*2); + }) + } + $scope.material.file5_url = urls; + } + }; + $scope.uploadFile6 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file6Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file6Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file6Progress.value = value/(files.length*2); + }) + } + $scope.material.file6_url = urls; + } + }; + $scope.submit = function (form) { + var codeKey = $scope.riskEvent.submit_url.substring($scope.riskEvent.submit_url.lastIndexOf('/') + 1); + $http.post('/risk/upload/' + codeKey, $scope.material).then(function (resp) { + commonDialog.alert({ + title: 'Success', + content: 'Submit successfully', + type: 'success' + }); + $state.go('analysis_monitoring.risk_business_bd'); + }, function (resp) { + commonDialog.alert({ + title: 'Error', + content: resp.data.message, + type: 'error' + }); + }); + } + } + ]); + + app.controller('auditMaterialCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', '$sce', 'commonDialog', + function ($scope, $state, $http, $uibModal, $filter, $sce, commonDialog) { + + // 一键下载功能 + $scope.downloadAsZip = function () { + return '/risk/business/' + $scope.riskEvent.risk_id + '/download/materialsAsZIP'; + }; + + // 加载提交材料 + // $scope.fileObject = {}; + $scope.loadRiskMaterial = function() { + $http.get('/risk/business/' + $scope.riskEvent.risk_id + '/material').then(function(resp) { + $scope.riskMaterial = resp.data; + $scope.file1 = resp.data.file1; + $scope.file2 = resp.data.file2; + $scope.file3 = resp.data.file3; + $scope.file4 = resp.data.file4; + $scope.file5 = resp.data.file5; + $scope.file6 = resp.data.file6; + + // for (var i = 1; i <= 10; i++) { + // var key = "file" + i; + // if (riskMaterial[key + '_url'] != null) + // $scope.fileObject[key] = riskMaterial[key + '_url']; + // } + // $scope.fileLength = Object.keys($scope.fileObject).length; + }) + }; + $scope.loadRiskMaterial(); + + // 材料审核 + $scope.auditMaterial = function(auditType) { + + var url = '/risk/business/events'; + var warningMessageHTML = '是否确定通过该材料?'; + if (auditType == 4) { + url = '/risk/business/' + $scope.riskEvent.risk_id + '/refuse'; + warningMessageHTML = '是否确定拒绝该材料?' + } + commonDialog.confirm({ + title: 'Warning', + contentHtml: $sce.trustAsHtml(warningMessageHTML) + }).then(function () { + $scope.riskEvent.result_type = auditType; + $http.put(url, $scope.riskEvent).then(function (resp) { + $state.go('^', {}, {reload: true}); + }, function (resp) { + commonDialog.alert({ + title: 'Error', + content: resp.data.message, + type: 'error' + }); + }); + }); + } + } + ]); + + app.controller('newRiskEventCtrl', ['$scope', '$state', '$http', '$uibModal', '$filter', 'commonDialog', + function ($scope, $state, $http, $uibModal, $filter, commonDialog) { + + $scope.today = new Date(); + $scope.orderTypes = orderTypesMap; + $scope.royapayOrderTypes = royalpayOrderTypesMap; + $scope.warningOrderTypes = warningOrderTypesMap; + + $scope.save = function(form) { + if (form.$invalid) { + angular.forEach(form, function (item, key) { + if (key.indexOf('$') < 0) { + item.$dirty = true; + } + }); + return; + } + + if ($scope.riskEvent.receive_email_date == null) + $scope.riskEvent.receive_email_date = new Date(); + // 默认设置邮件回复截止日期为收到邮件的七天后,如果是内部调单,设置三天后 + var replyDeadline = angular.copy($scope.riskEvent.receive_email_date); + if ($scope.riskEvent.order_type > 2) + replyDeadline.setDate(replyDeadline.getDate() + 3); + else + replyDeadline.setDate(replyDeadline.getDate() + 7); + $scope.riskEvent.reply_email_date = $filter('date')(replyDeadline, 'yyyy-MM-dd'); + + $scope.riskEvent.receive_email_date = $filter('date')($scope.riskEvent.receive_email_date, 'yyyy-MM-dd'); + + $http.post('/risk/business/events', $scope.riskEvent).then(function (resp) { + commonDialog.alert({ + title: 'Success', + content: 'Register new riskEvent successfully', + type: 'success' + }); + $state.go('^',{}, {reload: true}); + }, function (resp) { + commonDialog.alert({ + title: 'Error', + content: resp.data.message, + type: 'error' + }); + }); + }; + $scope.partnerParam = {}; + $scope.loadParnters = function() { + $scope.partnerParam.sub_merchant_id = $scope.riskEvent.sub_merchant_id; + $http.get('/risk/business/partners', {params: $scope.partnerParam}).then(function (resp) { + $scope.partners = resp.data; + $scope.riskEvent.client_moniker = $scope.partners[0].client_moniker; + if ($scope.partners != null && $scope.partners.length > 1 && $scope.riskEvent.order_type != 5) + commonDialog.confirm({ + title: 'Warning', + content: '该微信子商户号下有多个商户,是否将调单类型改为通用号调单?' + }).then(function () { + $scope.riskEvent.order_type = 5 + ""; + }); + + }); + } + } + ]); + + // 调单类型过滤器 + app.filter('orderType', function() { + return function(type) { + return orderTypesMap[type]; + } + }); + + app.filter('royalPayOrderType', function() { + return function(type) { + return royalpayOrderTypesMap[type]; + } + }); + + app.filter('warningOrderType', function() { + return function(type) { + return warningOrderTypesMap[type]; + } + }); + + // 处理结果过滤器 + app.filter('resultType', function() { + return function(type, resultTypesMap) { + return resultTypesMap[type]; + } + }); + // 邮件发送状态过滤器 + app.filter('emailStatus', function() { + return function(status) { + return emailStatusMap[status]; + } + }); return app; }); diff --git a/src/main/ui/static/analysis/templates/audit_material.html b/src/main/ui/static/analysis/templates/audit_material.html new file mode 100644 index 000000000..b62e28732 --- /dev/null +++ b/src/main/ui/static/analysis/templates/audit_material.html @@ -0,0 +1,159 @@ + +
    +
    Audit Files     + + 一键下载 + + 通过 + + 打回 + +
    +
    +
    +
    + +
    +
    +

    +
    +
    +
    +
    +
    +

    1、物流公司发货单据照片 + 要求:每笔交易对应的物流单必须提供,且单据必须清晰可见
    + 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. +

    +

    1.请解释相应的消费场景/业务模式,例如网站商城,扫码支付, 消费者到店支付等;
    + Please explain the relative payment scenario/business activities, for example, online store, QR code payment, payment at the store, etc; +

    +
    +
    +   
    +
    + + + +
    +
    +
    +
    +
    +

    2、用户购买虚拟物品需要提供聊天记录、订单信息、发货凭证截图、虚拟物品最终消费场景(例如何种游戏、软件)、提供消费场景网址/下载链接 + 要求:每笔交易对应的截图必须清晰可见
    + 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. +

    +

    2.提供相应购物清单,订单小票(请提供与被查交易订单号相匹配的交易时间及金额的发票);
    + Provide related shopping lists, invoices. (Please provide the invoices, amount of which matches that of the abnormal transaction); +

    +
    +
    +   
    +
    + + + +
    +
    + +
    +
    +
    +

    3、购物小票/发票存根照片 + 照片应清晰,必须显示商户名称、商户地址、购物时间、物品名称 + 购物金额等
    + 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. +

    +

    3.提供相应的发货证明,报关单(若有消费者在国内购买,请提供物流单据或报关单);
    + Relative proof of delivery, customs declaration (If the consumer purchased from China, please provide shipping receipt or customs declaration); +

    +
    +
    +   
    +
    + + + +
    +
    +
    +
    +
    +

    4、显示商户门牌号码和店头名称的照片 + 要求:清晰可见,至少一张
    + Photos of Merchant Street number & Merchant name + Requirement: At least one visible photo +

    +

    4.提供您的门店照片(门店照及店铺内的照片各一张, 一张可以看到商户名的门头照,一张可以看到相关商品或服务的店内照片);
    + Photos of the store ( one of each front-store and in-store); +

    +
    +
    +   
    +
    + + + +
    +
    + +
    +
    +
    +

    5、显示商户营业场景所场内部情况(如店内商品陈列、收银台等)的照片 + 要求:照片清晰,且能清楚显示商户实际售卖物品或服务,至少三张
    + 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 +

    +

    5.其他可以还原交易背景的资料,如和消费者的聊天记录等,来佐证被查单号交易的真实性;
    + Other materials that can verify the payment scenario, for example, chatting history, to prove the truth of the transactions; +

    +
    +
    +   
    +
    + + + +
    +
    + +
    +
    +
    +

    6、其他图片
    + Other pictures +

    +
    +
    +   
    +
    + + + +
    +
    + +
    + +
    +
    +
    diff --git a/src/main/ui/static/analysis/templates/bd_upload_material.html b/src/main/ui/static/analysis/templates/bd_upload_material.html new file mode 100644 index 000000000..00983f23b --- /dev/null +++ b/src/main/ui/static/analysis/templates/bd_upload_material.html @@ -0,0 +1,203 @@ +
    + + +
    +
    +
    +
    +
    +

    + 1、物流公司发货单据照片 + 要求:每笔交易对应的物流单必须提供,且单据必须清晰可见
    + 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. +

    +

    + 1.请解释相应的消费场景/业务模式,例如网站商城,扫码支付, 消费者到店支付等;
    + Please explain the relative payment scenario/business activities, + for example, online store, QR code payment, payment at the store, etc; +

    +
    +
    +
    + +
    +   
    +
    + {{file1Progress.value}}% + +
    +
    +
    +
    +
    +

    + 2、用户购买虚拟物品需要提供聊天记录、订单信息、发货凭证截图、 + 虚拟物品最终消费场景(例如何种游戏、软件)、提供消费场景网址/下载链接 + 要求:每笔交易对应的截图必须清晰可见
    + 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. +

    +

    + 2.提供相应购物清单,订单小票(请提供与被查交易订单号相匹配的交易时间及金额的发票);
    + Provide related shopping lists, invoices. + (Please provide the invoices, amount of which matches that of the abnormal transaction); +

    +
    +
    +
    + +
    +   
    +
    + {{file2Progress.value}}% + +
    +
    + +
    +
    +
    +

    + 3、购物小票/发票存根照片 + 照片应清晰,必须显示商户名称、商户地址、购物时间、物品名称 + 购物金额等
    + 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. +

    +

    + 3.提供相应的发货证明,报关单(若有消费者在国内购买,请提供物流单据或报关单);
    + Relative proof of delivery, customs declaration + (If the consumer purchased from China, please provide shipping receipt or customs declaration); +

    +
    +
    +
    + +
    +   
    +
    + {{file3Progress.value}}% + +
    +
    +
    +
    +
    +

    + 4、显示商户门牌号码和店头名称的照片 + 要求:清晰可见,至少一张
    + Photos of Merchant Street number & Merchant name + Requirement: At least one visible photo +

    +

    + 4.提供您的门店照片(门店照及店铺内的照片各一张, 一张可以看到商户名的门头照,一张可以看到相关商品或服务的店内照片);
    + Photos of the store ( one of each front-store and in-store); +

    +
    +
    +
    + +
    +   
    +
    + {{file4Progress.value}}% + +
    +
    + +
    +
    +
    +

    + 5、显示商户营业场景所场内部情况(如店内商品陈列、收银台等)的照片 + 要求:照片清晰,且能清楚显示商户实际售卖物品或服务,至少三张
    + 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 +

    +

    + 5.其他可以还原交易背景的资料,如和消费者的聊天记录等,来佐证被查单号交易的真实性;
    + Other materials that can verify the payment scenario, for example, + chatting history, to prove the truth of the transactions; +

    +
    +
    +
    + +
    +   
    +
    + {{file5Progress.value}}% + +
    +
    + +
    +
    +
    +

    6、其他图片
    + Other pictures +

    +
    +
    +
    + +
    +   
    +
    + {{file6Progress.value}}% + +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    + Submit +
    + +
    \ No newline at end of file diff --git a/src/main/ui/static/analysis/templates/new_riskEvent.html b/src/main/ui/static/analysis/templates/new_riskEvent.html new file mode 100644 index 000000000..8aece4787 --- /dev/null +++ b/src/main/ui/static/analysis/templates/new_riskEvent.html @@ -0,0 +1,223 @@ +
    +

    New RiskEvent

    +
    + +
    +
    +
    +
    +
    +
    RiskEvent Basic Information
    +
    +
    +
    + +
    + +
    +

    required field +

    +
    +
    +
    +
    + +
    + +
    +

    required field +

    +
    +
    +
    +
    + +
    + +
    +

    required field +

    +
    +
    +
    + +
    + +
    + +
    +

    required field +

    +
    +
    +
    + +
    + +
    + + +
    +

    required field +

    +
    +
    +
    + +
    + +
    + +
    +

    Required Field +

    +

    Less Than 6 Letters +

    +

    + Only Uppercase Letters and Numbers are allowed +

    +
    +
    +
    + +
    + +
    + +
    +

    required field +

    +
    +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    diff --git a/src/main/ui/static/analysis/templates/riskEvent_detail.html b/src/main/ui/static/analysis/templates/riskEvent_detail.html index 7f23e9322..6245c3e35 100644 --- a/src/main/ui/static/analysis/templates/riskEvent_detail.html +++ b/src/main/ui/static/analysis/templates/riskEvent_detail.html @@ -1,10 +1,479 @@ - - - - - Title - - -欢迎来到riskDetail页面 - - \ No newline at end of file +
    +
    +
    + +
    +
    +
    diff --git a/src/main/ui/static/analysis/templates/riskEvent_detail_bd.html b/src/main/ui/static/analysis/templates/riskEvent_detail_bd.html new file mode 100644 index 000000000..66a7e1e6b --- /dev/null +++ b/src/main/ui/static/analysis/templates/riskEvent_detail_bd.html @@ -0,0 +1,256 @@ +
    +
    +
    + +
    +
    +

    + Orders Information +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Order IDAmountInput AmountAUD AmountExchange RateStatusCreate TimeGateway
    {{trade.order_id}}{{trade.total_amount | currency: trade.currency}} + + + - +
    +
    +
    + + 返回 + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    + 1、物流公司发货单据照片 + 要求:每笔交易对应的物流单必须提供,且单据必须清晰可见
    + 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. +

    +

    + 1.请解释相应的消费场景/业务模式,例如网站商城,扫码支付, 消费者到店支付等;
    + Please explain the relative payment scenario/business activities, + for example, online store, QR code payment, payment at the store, etc; +

    +
    +
    +
    + +
    +   
    +
    + {{file1Progress.value}}% + +
    +
    +
    +
    +
    +

    + 2、用户购买虚拟物品需要提供聊天记录、订单信息、发货凭证截图、 + 虚拟物品最终消费场景(例如何种游戏、软件)、提供消费场景网址/下载链接 + 要求:每笔交易对应的截图必须清晰可见
    + 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. +

    +

    + 2.提供相应购物清单,订单小票(请提供与被查交易订单号相匹配的交易时间及金额的发票);
    + Provide related shopping lists, invoices. + (Please provide the invoices, amount of which matches that of the abnormal transaction); +

    +
    +
    +
    + +
    +   
    +
    + {{file2Progress.value}}% + +
    +
    + +
    +
    +
    +

    + 3、购物小票/发票存根照片 + 照片应清晰,必须显示商户名称、商户地址、购物时间、物品名称 + 购物金额等
    + 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. +

    +

    + 3.提供相应的发货证明,报关单(若有消费者在国内购买,请提供物流单据或报关单);
    + Relative proof of delivery, customs declaration + (If the consumer purchased from China, please provide shipping receipt or customs declaration); +

    +
    +
    +
    + +
    +   
    +
    + {{file3Progress.value}}% + +
    +
    +
    +
    +
    +

    + 4、显示商户门牌号码和店头名称的照片 + 要求:清晰可见,至少一张
    + Photos of Merchant Street number & Merchant name + Requirement: At least one visible photo +

    +

    + 4.提供您的门店照片(门店照及店铺内的照片各一张, 一张可以看到商户名的门头照,一张可以看到相关商品或服务的店内照片);
    + Photos of the store ( one of each front-store and in-store); +

    +
    +
    +
    + +
    +   
    +
    + {{file4Progress.value}}% + +
    +
    + +
    +
    +
    +

    + 5、显示商户营业场景所场内部情况(如店内商品陈列、收银台等)的照片 + 要求:照片清晰,且能清楚显示商户实际售卖物品或服务,至少三张
    + 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 +

    +

    + 5.其他可以还原交易背景的资料,如和消费者的聊天记录等,来佐证被查单号交易的真实性;
    + Other materials that can verify the payment scenario, for example, + chatting history, to prove the truth of the transactions; +

    +
    +
    +
    + +
    +   
    +
    + {{file5Progress.value}}% + +
    +
    + +
    +
    +
    +

    6、其他图片
    + Other pictures +

    +
    +
    +
    + +
    +   
    +
    + {{file6Progress.value}}% + +
    +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    + +
    + Submit +
    + +
    diff --git a/src/main/ui/static/analysis/templates/risk_business.html b/src/main/ui/static/analysis/templates/risk_business.html index 212297aca..27351ff86 100644 --- a/src/main/ui/static/analysis/templates/risk_business.html +++ b/src/main/ui/static/analysis/templates/risk_business.html @@ -1,23 +1,166 @@ -
    +
    -
    -
    - - +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    + +
    -
    - + +
    + + + New Event +
    -
    +

    RiskEvent List

    @@ -26,63 +169,72 @@
    - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + +
    Partner Codeorder_typeSub Merchant IDEmail StatusFillIn PersonDescriptionCreate TimeOperation
    PartnerOrder TypeResult TypeSub Merchant IDEmail StatusDescriptionChannel ResultRisk ManagerCreate TimeOperation
    - 未发送 - 已发送 - - - Detail - -
    + + {{riskEvent.short_name}}({{riskEvent.client_moniker}}) + + + {{riskEvent.result_type | resultType:resultSearchTypes}} + + + Detail + +
    -
    - - -
    -
    -
    \ No newline at end of file diff --git a/src/main/ui/static/analysis/templates/risk_business_bd.html b/src/main/ui/static/analysis/templates/risk_business_bd.html new file mode 100644 index 000000000..271756341 --- /dev/null +++ b/src/main/ui/static/analysis/templates/risk_business_bd.html @@ -0,0 +1,181 @@ +
    +
    +
    +
    +
    +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + + +
    + +
    + +
    +
    + +
    + +
    +
    +
    +
    + +
    +
    +

    RiskEvent List

    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PartnerOrder TypeResult TypeSub Merchant IDDescriptionChannel ResultRisk ManagerCreate TimeOperation
    + + {{riskEvent.short_name}}({{riskEvent.client_moniker}}) + + + {{riskEvent.result_type | resultType:resultTypesForBD}} + + + Detail + +
    +
    + + +
    +
    +
    +
    +
    diff --git a/src/main/ui/static/riskupload/risk_upload.js b/src/main/ui/static/riskupload/risk_upload.js new file mode 100644 index 000000000..ed756391a --- /dev/null +++ b/src/main/ui/static/riskupload/risk_upload.js @@ -0,0 +1,170 @@ +// define(['angular', 'static/commons/commons', 'uiBootstrap', 'uiRouter', 'ngBootSwitch', 'ngFileUpload'], function (angular) { +// 'use strict'; + var app = angular.module('riskUploadApp',['ngFileUpload','ui.bootstrap']); + app.controller('riskUploadCtrl', ['$scope','$http','Upload','$filter',function ($scope,$http,Upload,$filter) { + $scope.material={}; + $scope.codeKey=document.getElementById('codeKey').value; + $scope.material.risk_id=document.getElementById('risk_id').value; + $scope.order_type=document.getElementById('order_type').value; + $scope.material.update_time=$filter('date')(new Date(), 'yyyy-MM-dd HH:mm:ss'); + $scope.uploadFile1 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file1Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file1Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file1Progress.value = value/(files.length*2); + }) + } + $scope.material.file1_url = urls; + } + }; + $scope.uploadFile2 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file2Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file2Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file2Progress.value = value/(files.length*2); + }) + } + $scope.material.file2_url = urls; + } + }; + $scope.uploadFile3 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file3Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file3Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file3Progress.value = value/(files.length*2); + }) + } + $scope.material.file3_url = urls; + } + }; + $scope.uploadFile4 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file4Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file4Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file4Progress.value = value/(files.length*2); + }) + } + $scope.material.file4_url = urls; + } + }; + + $scope.uploadFile5 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file5Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file5Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file5Progress.value = value/(files.length*2); + }) + } + $scope.material.file5_url = urls; + } + }; + + $scope.uploadFile6 = function (files) { + if (files && files.length) { + var urls = new Array(); + var value = 0; + $scope.file6Progress = {value: 0}; + for (var i = 0; i < files.length; i++) { + var file = files[i]; + Upload.upload({ + url: '/attachment/riskFiles', + data: {file: file} + }).then(function (resp) { + urls.push(resp.data.url); + }, function (resp) { + delete $scope.file6Progress; + alert('Upload Failed'); + }, function (evt) { + value += parseInt(100 * evt.loaded / evt.total ); + $scope.file6Progress.value = value/(files.length*2); + }) + } + $scope.material.file6_url = urls; + } + }; + $scope.submit = function (form) { + $http.post('/risk/upload/'+$scope.codeKey, $scope.material).then(function (resp) { + // commonDialog.alert({title: 'Success', content: 'Submit successfully', type: 'success'}); + alert('Submit successfully'); + window.location.href="/risk_upload_success.html"; + }, function (resp) { + alert('Submit failed'); + // commonDialog.alert({title: 'Error', content: resp.data.message, type: 'error'}); + }); + } + + }]); + + + + +// return app; +// +// }) + +