From 58a8acceb63d52377da4b837e859f2e03d2bce1b Mon Sep 17 00:00:00 2001
From: gongzuming <gongzuming>
Date: 星期一, 14 十月 2024 08:44:40 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master-v3' into master-v2

---
 src/main/java/com/mzl/flower/service/payment/UserPaymentSybService.java |  600 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 600 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/mzl/flower/service/payment/UserPaymentSybService.java b/src/main/java/com/mzl/flower/service/payment/UserPaymentSybService.java
new file mode 100644
index 0000000..39da81a
--- /dev/null
+++ b/src/main/java/com/mzl/flower/service/payment/UserPaymentSybService.java
@@ -0,0 +1,600 @@
+package com.mzl.flower.service.payment;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.mzl.flower.config.SybPaymentProperties;
+import com.mzl.flower.config.exception.ValidationException;
+import com.mzl.flower.config.security.SecurityUtils;
+import com.mzl.flower.constant.Constants;
+import com.mzl.flower.dto.request.payment.UserPaymentDTO;
+import com.mzl.flower.entity.flower.Flower;
+import com.mzl.flower.entity.payment.*;
+import com.mzl.flower.mapper.flower.FlowerMapper;
+import com.mzl.flower.mapper.payment.*;
+import com.mzl.flower.pay.SybConstants;
+import com.mzl.flower.pay.SybPayService;
+import com.mzl.flower.pay.SybUtil;
+import com.mzl.flower.service.BaseService;
+import com.mzl.flower.service.coupon.CouponRecordService;
+import com.mzl.flower.service.flower.FlowerService;
+import com.mzl.flower.service.point.PointGoodsService;
+import com.mzl.flower.utils.UUIDGenerator;
+import io.micrometer.core.instrument.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+@Service
+@Transactional
+@Slf4j
+public class UserPaymentSybService extends BaseService {
+
+    @Autowired
+    private UserPaymentMapper userPaymentMapper;
+
+    @Autowired
+    private RedisLockService lockService;
+
+    @Autowired
+    private OrderMapper orderMapper;
+
+    @Autowired
+    private OrderItemMapper orderItemMapper;
+
+    @Autowired
+    private FlowerMapper flowerMapper;
+
+    @Autowired
+    private FlowerService flowerService;
+
+    @Autowired
+    private DeliveryOrderService deliveryOrderService;
+
+    @Autowired
+    private OrderRefundMapper orderRefundMapper;
+
+    @Autowired
+    private OrderPointGoodsMapper orderPointGoodsMapper;
+
+    @Autowired
+    private PointGoodsService pointGoodsService;
+
+    @Autowired
+    private CouponRecordService couponRecordService;
+
+    @Autowired
+    private OrderService orderService;
+
+    @Autowired
+    private SybPaymentProperties sybPaymentProperties;
+
+    /**
+     * 通联支付
+     *
+     * @param order
+     * @return
+     */
+    public Map prepay(Order order) throws Exception {
+        String userId = SecurityUtils.getUserId();
+        UserPayment up = prepareUserPayment(userId, order);
+        log.info("UserPayment: " + toJSONString(up));
+
+        SybPayService service = new SybPayService();
+        long trxamt = prepareAmount(up.getPaymentAmount());
+        String reqsn = order.getId();
+        String unireqsn = "";
+        String body = "通联支付";
+        String remark = "通联支付";
+        String notifyUrl = sybPaymentProperties.getCallBackUrl() + "/flower/api/ua/notify/syb/paid";
+
+        // 调用接口
+        Map map = service.createOrder(trxamt, reqsn, unireqsn, body, remark, notifyUrl);
+
+        up.setPrepayResponse(toJSONString(map));
+
+        userPaymentMapper.insert(up);
+
+        return map;
+    }
+
+    private Integer prepareAmount(BigDecimal amount){
+         return amount.multiply(new BigDecimal(100)).intValue();
+    }
+
+    private UserPayment prepareUserPayment(String userId, Order order){
+        UserPayment up = new UserPayment();
+        up.setId(UUIDGenerator.getUUID());
+        up.setUserId(userId);
+        up.setOrderId(order.getId());
+        up.setPaymentAmount(order.getTotalAmount());
+        up.setPaymentTime(LocalDateTime.now());
+        up.create(userId);
+
+        return up;
+    }
+
+    /**
+     * 动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容由于收银宝加字段而引起的签名异常
+     * @param request
+     * @return
+     */
+    private TreeMap<String, String> getParams(HttpServletRequest request){
+        TreeMap<String, String> map = new TreeMap<>();
+        Map reqMap = request.getParameterMap();
+        for(Object key:reqMap.keySet()){
+            String value = ((String[])reqMap.get(key))[0];
+            System.out.println(key+";"+value);
+            map.put(key.toString(),value);
+        }
+        return map;
+    }
+
+    public String handlePayCallback(HttpServletRequest request) {
+        try {
+            request.setCharacterEncoding("UTF-8");//通知传输的编码为GBK
+            TreeMap<String,String> params = getParams(request);//动态遍历获取所有收到的参数,此步非常关键,因为收银宝以后可能会加字段,动态获取可以兼容
+
+            String appkey = "";
+            if("RSA".equals(params.get("signtype")))
+                appkey = SybConstants.SYB_RSATLPUBKEY;
+            else if("SM2".equals(params.get("signtype")))
+                appkey = SybConstants.SYB_SM2TLPUBKEY;
+            else
+                appkey = SybConstants.SYB_MD5_APPKEY;
+            boolean isSign = SybUtil.validSign(params, appkey, params.get("signtype"));// 接受到推送通知,首先验签
+            log.info("验签结果:" + isSign);
+
+            //验签完毕进行业务处理
+            if(isSign){
+                String originalXml = toJSONString(params);
+                log.info("transaction: " + originalXml);
+                String outTradeNo = params.get("cusorderid");//统一下单对应的reqsn订单号
+                String transactionId = params.get("trxid");//通联收银宝交易流水号
+                String orderId = outTradeNo;
+                Integer trxamt = Integer.parseInt(params.get("trxamt"));//交易金额 单位:分
+                log.info("======trxamt: " + trxamt);
+
+                String trxstatus = params.get("trxstatus");//支付状态
+                String status = Constants.PAYMENT_STATUS.FAILED.name();
+                //交易状态详见交易返回码说明
+                /*0000:交易成功
+                错误码为空: 交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(例如30秒)查询交易
+                1001:交易不存在
+                2008或者2000 : 交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易
+                3开头的错误码代表交易失败
+                3888-流水号重复
+                3889-交易控制失败,具体原因看errmsg
+                3099-渠道商户错误
+                3014-交易金额小于应收手续费
+                3031-校验实名信息失败
+                3088-交易未支付(在查询时间区间内未成功支付,如已影响资金24小时内会做差错退款处理)
+                3089-撤销异常,如已影响资金24小时内会做差错退款处理
+                3045-其他错误,具体原因看errmsg
+                3999-其他错误,具体原因看errmsg
+                其他3开头的错误码代表交易失败,具体原因请读取errmsg
+                */
+                if ("0000".equals(trxstatus)){
+                    status = Constants.PAYMENT_STATUS.SUCCESS.name();
+                }
+
+                UserPaymentDTO dto = new UserPaymentDTO();
+                dto.setOrderId(orderId);
+                dto.setTransactionId(transactionId);
+                dto.setOutTradeNo(outTradeNo);
+                dto.setOriginalXml(originalXml);
+                dto.setPaymentAmountCallback(trxamt + "");
+                dto.setStatus(status);
+
+                saveCallbackInfo(dto, Constants.ORDER_STATUS_BACKEND.PAYMENT.name());
+            }
+        } catch (Exception e) {
+            log.error("解析付款通知出错:{}", e.getMessage(), e);
+        }
+
+        return "success";
+    }
+
+    public void saveCallbackInfo(UserPaymentDTO dto, String orderStatus){
+        String orderId = dto.getOrderId();
+        boolean lock = lockService.getObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId);
+        if(!lock){
+            return;
+        }
+
+        try {
+            UserPayment up = userPaymentMapper.selectOne(
+                    new QueryWrapper<UserPayment>().eq("order_id", orderId));
+            BeanUtils.copyProperties(dto, up);
+
+            up.setPaymentTimeCallback(LocalDateTime.now());
+            userPaymentMapper.updateById(up);
+
+            Order order = orderMapper.selectById(orderId);
+            if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) {
+                order.setPaymentTrId(dto.getTransactionId());
+                if (up.getPaymentAmountCallback() != null) {
+                    order.setPaymentAmount(new BigDecimal(up.getPaymentAmountCallback()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));//转换成单位元
+                } else {
+                    order.setPaymentAmount(order.getTotalAmount());
+                }
+                order.setPaymentTime(up.getPaymentTimeCallback());
+                order.setStatus(Constants.ORDER_STATUS.SEND.name());
+            } else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){
+                order.setStatus(Constants.ORDER_STATUS.CANCEL.name());
+            }
+            order.setStatusBackend(orderStatus);
+            orderMapper.updateById(order);
+
+            if(Constants.ORDER_STATUS_BACKEND.PAYMENT.name().equals(orderStatus)) {
+                postPayment(order);
+            } else if (Constants.ORDER_STATUS_BACKEND.CANCEL.name().equals(orderStatus)){
+                releasePrepayLock(order);
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        } finally {
+            lockService.releaseObjectLock(RedisLockService.LOCK_KEY_PAYMENT_NOTIFY_, orderId);
+        }
+    }
+
+    private void postPayment(Order order){
+        log.info("回调后处理订单信息:" + toJSONString(order));
+        //创建配送单
+        deliveryOrderService.createDeliveryOrder(order);
+        flowerService.updateFlowerSales(order);
+    }
+
+    public boolean checkOrderStatusPayAgain(String outTradeNo) throws Exception {
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", outTradeNo));
+        String s = up.getStatus();
+        if(StringUtils.isNotEmpty(s)){
+            return true;
+        }
+
+        SybPayService service = new SybPayService();
+        Map<String,String> params = service.query(outTradeNo, up.getTransactionId());
+
+        String originalXml = toJSONString(params);//回调请求内容
+        log.info("message: " + originalXml);
+        String transactionId = params.get("trxid");//通联收银宝交易流水号
+        String orderId = outTradeNo;
+
+        String trxcode = params.get("trxcode");
+        /* 交易类型
+        VSP501:微信支付
+        VSP502:微信支付撤销
+        VSP503:微信支付退款
+        */
+        String trxstatus = params.get("trxstatus");//支付状态
+        if("VSP501".equals(trxcode)){
+            if("0000".equals(trxstatus)) {
+                String status = Constants.PAYMENT_STATUS.SUCCESS.name();
+                UserPaymentDTO dto = new UserPaymentDTO();
+                dto.setOrderId(orderId);
+                dto.setTransactionId(transactionId);
+                dto.setOutTradeNo(outTradeNo);
+                dto.setOriginalXml(originalXml);
+                String trxamtStr = params.get("trxamt");//交易金额 单位:分
+                log.info("======trxamt: " + trxamtStr);
+                dto.setPaymentAmountCallback(trxamtStr);
+
+                dto.setStatus(status);
+
+                String orderStatus = Constants.ORDER_STATUS_BACKEND.PAYMENT.name();
+
+                saveCallbackInfo(dto, orderStatus);
+
+                return true;
+            }
+        }
+
+        return !("1001".equals(trxstatus) || StringUtils.isEmpty(trxstatus)
+                || "2008".equals(trxstatus)
+                || "2000".equals(trxstatus));
+    }
+
+    public boolean checkOrderStatusRefund(String outTradeNo) throws Exception {
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", outTradeNo));
+        String s = up.getStatus();
+        if(StringUtils.isEmpty(s)){
+            return true;
+        }
+
+        SybPayService service = new SybPayService();
+        Map<String,String> params = service.query(outTradeNo, up.getTransactionId());
+
+        String originalXml = toJSONString(params);//回调请求内容
+        log.info("message: " + originalXml);
+
+        String trxcode = params.get("trxcode");
+        /* 交易类型
+        VSP501:微信支付
+        VSP502:微信支付撤销
+        VSP503:微信支付退款
+        */
+        if ("VSP502".equals(trxcode) || "VSP503".equals(trxcode)){
+            String trxid = params.get("trxid");
+            updateOrderRefund(outTradeNo, trxid);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    public void cancelOrder(String orderId) throws Exception {
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", orderId));
+        if(StringUtils.isNotEmpty(up.getStatus())){
+            throw new ValidationException("订单不可取消");
+        }
+
+        SybPayService service = new SybPayService();
+
+        Map<String,String> params = service.query(orderId, up.getTransactionId());
+        String retcode = params.get("retcode");
+        if(!"SUCCESS".equals(retcode)){
+            throw new ValidationException("查询订单交易状态失败: " + params.get("retmsg"));
+        }
+        String trxstatus = params.get("trxstatus");//支付状态
+
+        if("1001".equals(trxstatus) || "3088".equals(trxstatus)){
+            //3088-交易未支付(在查询时间区间内未成功支付,如已影响资金24小时内会做差错退款处理)
+            //1001:交易不存在
+            //直接取消
+        } else if (StringUtils.isEmpty(trxstatus)
+                || "2008".equals(trxstatus)
+                || "2000".equals(trxstatus)) {
+            String trxid = params.get("trxid");
+            //2008或者2000 : 交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易
+            Map<String,String> map = service.close(trxid, orderId);
+            log.info("======关闭订单结果" + toJSONString(map));
+
+            retcode = map.get("retcode");
+            if(!"SUCCESS".equals(retcode)){
+                throw new ValidationException("调用通联关闭订单失败: " + map.get("retmsg"));
+            }
+            trxstatus = map.get("trxstatus");
+            if(!"0000".equals(trxstatus)){
+                throw new ValidationException("取消订单失败: " + map.get("errmsg"));
+            }
+
+        } else {
+            throw new ValidationException("订单不可取消");
+        }
+
+        Order order = orderMapper.selectById(orderId);
+        order.setStatus(Constants.ORDER_STATUS.CANCEL.name());
+        order.setStatusBackend(Constants.ORDER_STATUS_BACKEND.CANCEL.name());
+        order.setCancelTime(LocalDateTime.now());
+        order.update(SecurityUtils.getUserId());
+        orderMapper.updateById(order);
+
+        up.setStatus(Constants.PAYMENT_STATUS.CLOSED.name());
+        up.update(SecurityUtils.getUserId());
+        userPaymentMapper.updateById(up);
+
+        releasePrepayLock(order);
+    }
+
+    private void releasePrepayLock(Order order){
+        log.info("恢复库存 积分商品兑换券 优惠券: " + order);
+        revertFlowerStock(order.getId());
+
+        //恢复积分商品兑换券
+        revertPointGoodsRecord(order.getId());
+
+        //恢复优惠券
+        String memberCouponId = order.getMemberCouponId();
+        if(StringUtils.isNotEmpty(memberCouponId)) {
+            couponRecordService.cancelCouponUsage(order.getId());
+        }
+    }
+
+    private void revertPointGoodsRecord(String orderId){
+        List<OrderPointGoods> ls = orderPointGoodsMapper.selectList(new QueryWrapper<OrderPointGoods>()
+                .eq("order_id", orderId));
+        if(ls != null && ls.size() > 0){
+            for(OrderPointGoods pg : ls){
+                pointGoodsService.revertExchangeGoods(pg.getGoodsRecordId());
+            }
+        }
+    }
+
+    public Map payAgain(String orderId){
+        // 获取订单里面的商品是否有限购的,如果有则判断是否已经超过限购数量
+        List<OrderItem> orderItemList = orderItemMapper.selectList(new QueryWrapper<OrderItem>()
+                .eq("order_id", orderId));
+
+        orderItemList.forEach(orderItem -> {
+            // 限购数量 鲜花数量校验
+            Integer completeNumToday=orderService.getFlowerCompleteNumToday(orderItem.getCreateBy(),orderItem.getFlowerId());
+            Integer tmp=completeNumToday+orderItem.getNum();
+            Flower flower=flowerMapper.selectById(orderItem.getFlowerId());
+            if(null!=flower.getLimited() && tmp.compareTo(flower.getLimited())>0){
+                throw new ValidationException("商品:'"+flower.getName()+"' 昨天17:00到今天17:00 超过限售数量:"+flower.getLimited()+"!");
+            }
+        });
+
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", orderId));
+        if(StringUtils.isNotEmpty(up.getStatus())){
+            throw new ValidationException("订单不可再支付");
+        }
+
+        return parseObject(up.getPrepayResponse(), TreeMap.class);
+    }
+
+    public synchronized void revertFlowerStock(String orderId){
+        List<OrderItem> ls = orderItemMapper.selectList(new QueryWrapper<OrderItem>()
+                .eq("order_id", orderId));
+        for(OrderItem c : ls){
+            flowerMapper.addFlowerStock(c.getFlowerId(), c.getNum());
+        }
+    }
+
+    public void refundOrderCustomer(String orderId) throws Exception {
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", orderId));
+        if(up.getPaymentAmount() == null){
+            throw new ValidationException("订单不可退款");
+        }
+
+        long trxamt = prepareAmount(up.getPaymentAmount());
+        String reqsn = orderId;
+        String oldtrxid = up.getTransactionId();
+        String oldreqsn = orderId;
+
+        SybPayService service = new SybPayService();
+        Map<String,String> map = service.cancel(trxamt, reqsn, oldtrxid, oldreqsn);
+        String retcode = map.get("retcode");
+        if(!"SUCCESS".equals(retcode)){
+            throw new ValidationException("调用通联撤销接口失败: " + map.get("retmsg"));
+        }
+
+        //这个不是订单状态,是通用的,如果是支付查询,代表就是订单状态,如果是退款代表的是退款状态
+        String trxstatus = map.get("trxstatus");
+        if(!"0000".equals(trxstatus)){
+            log.error("通联撤销交易失败:" + map.get("errmsg"));
+            throw new ValidationException("订单退款交易失败:" + map.get("errmsg"));
+        }
+
+        updateOrderRefund(orderId, oldtrxid);
+    }
+
+    /**
+     * 退款
+     *
+     * @param orderId
+     */
+    public void refundOrder(String orderId) throws Exception {
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", orderId));
+        if(up.getPaymentAmount() == null){
+            throw new ValidationException("订单不可退款");
+        }
+
+        long trxamt = prepareAmount(up.getPaymentAmount());
+        String reqsn = orderId;
+        String oldtrxid = up.getTransactionId();
+        String oldreqsn = orderId;
+
+        SybPayService service = new SybPayService();
+        Map<String,String> map = service.refund(trxamt, reqsn, oldtrxid, oldreqsn);
+        String retcode = map.get("retcode");
+        if(!"SUCCESS".equals(retcode)){
+            throw new ValidationException("调用通联退款失败: " + map.get("retmsg"));
+        }
+
+        //这个不是订单状态,是通用的,如果是支付查询,代表就是订单状态,如果是退款代表的是退款状态
+        String trxstatus = map.get("trxstatus");
+        if(!"0000".equals(trxstatus)){
+            throw new ValidationException("订单退款交易失败:" + map.get("errmsg"));
+        }
+
+        updateOrderRefund(orderId, oldtrxid);
+    }
+
+    private void updateOrderRefund(String orderId, String refundNo){
+        Order o = orderMapper.selectById(orderId);
+
+        if(Constants.ORDER_STATUS.REFUND.name().equals(o.getStatus())){
+            return;
+        }
+
+        o.setRefundAmount(o.getPaymentAmount());
+        o.setRefundNo(refundNo);
+        o.setRefundTime(LocalDateTime.now());
+
+        o.setStatus(Constants.ORDER_STATUS.REFUND.name());
+        o.setStatusBackend(Constants.ORDER_STATUS_BACKEND.REFUND.name());
+        o.update("sys");
+
+        orderMapper.updateById(o);
+
+        deliveryOrderService.refundDelete(orderId);
+
+        releasePrepayLock(o);
+    }
+
+    public String refundOrderSub(Order o, BigDecimal refundAmount) throws Exception {
+        if(o.getPaymentTime() == null || o.getPaymentAmount() == null){
+            throw new ValidationException("未支付订单不可退款");
+        }
+
+        if(o.getRefundTime() != null){
+            throw new ValidationException("已全额退款订单不可退款");
+        }
+
+        if (refundAmount == null || refundAmount.doubleValue() == 0) {
+            throw new ValidationException("退款金额不能为空");
+        }
+
+        List<OrderRefund> reLs = orderRefundMapper.selectList(new QueryWrapper<OrderRefund>()
+                .eq("order_id", o.getId()));
+        BigDecimal rra = new BigDecimal(0);
+        if(reLs != null && reLs.size() > 0){
+            for(OrderRefund r : reLs){
+                rra = rra.add(r.getRefundAmount());
+            }
+        }
+
+        long total = (long)prepareAmount(o.getPaymentAmount());
+        long refund = (long)prepareAmount(refundAmount);
+
+        long rraa = (long)prepareAmount(rra);
+        if(rraa + refund > total){
+            throw new ValidationException("退款金额不能大于订单金额");
+        }
+
+        OrderRefund re = new OrderRefund();
+        re.setId(UUIDGenerator.getUUID());
+        re.setOrderId(o.getId());
+        re.setOrderAmount(o.getPaymentAmount());
+        re.setRefundAmount(refundAmount);
+
+        UserPayment up = userPaymentMapper.selectOne(
+                new QueryWrapper<UserPayment>().eq("order_id", o.getId()));
+
+        long trxamt = refund;
+        String reqsn = o.getId();
+        String oldtrxid = up.getTransactionId();
+        String oldreqsn = null;
+
+        SybPayService service = new SybPayService();
+        Map<String,String> map = service.refund(trxamt, reqsn, oldtrxid, oldreqsn);
+        String retcode = map.get("retcode");
+        if(!"SUCCESS".equals(retcode)){
+            throw new ValidationException("调用通联退款失败: " + map.get("retmsg"));
+        }
+
+        //这个不是订单状态,是通用的,如果是支付查询,代表就是订单状态,如果是退款代表的是退款状态
+        String trxstatus = map.get("trxstatus");
+        if(!"0000".equals(trxstatus)){
+            throw new ValidationException("订单退款交易失败:" + map.get("errmsg"));
+        }
+
+        re.setRequest(toJSONString(map));
+        re.create(SecurityUtils.getUserId());
+
+        re.setStatus(retcode);
+        re.setNotification(retcode);
+        re.setNotifyTime(LocalDateTime.now());
+
+        orderRefundMapper.insert(re);
+
+        return re.getId();
+    }
+
+}

--
Gitblit v1.9.3