3 files added
13 files modified
335 ■■■■■ changed files
src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java 12 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/AdminSystemController.java 14 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java 12 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java 45 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java 3 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/dto/PayLinkDto.java 17 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/dto/RegisterDto.java 22 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/dto/XtPayCreateOrderDto.java 18 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java 30 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java 6 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java 29 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/order/orderList.html 16 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/system/payLink.html 105 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/mall/controller/AdminMallOrderController.java
@@ -135,6 +135,18 @@
    }
    /**
     * 订单列表-确认收款
     *
     * @param id
     * @return
     */
    @GetMapping("insurePay/{id}")
    @ControllerEndpoint(operation = "订单列表-确认收款", exceptionMessage = "操作失败")
    public FebsResponse insurePay(@NotNull(message = "{required}") @PathVariable Long id) {
        return adminMallOrderService.insurePay(id);
    }
    /**
     * 订单列表-取消发货
     *
     * @param id
src/main/java/cc/mrbird/febs/mall/controller/AdminSystemController.java
@@ -136,6 +136,20 @@
        return new FebsResponse().success().message("保存成功");
    }
    @PostMapping(value = "/payLink")
    @ControllerEndpoint(operation = "保存付款链接配置")
    public FebsResponse payLink(PayLinkDto dto) {
        if (StrUtil.isBlank(dto.getXtLink())) {
            return new FebsResponse().fail().message("付款链接不能为空");
        }
        if (StrUtil.isBlank(dto.getXtLinkImg())) {
            return new FebsResponse().fail().message("付款链接图片不能为空");
        }
        commonService.addDataDic("PAY_LINK", "XT_LINK", dto.getXtLink(), "付款链接", false);
        commonService.addDataDic("PAY_LINK", "XT_LINK_IMG", dto.getXtLinkImg(), "付款链接图片", false);
        return new FebsResponse().success().message("保存成功");
    }
    @GetMapping(value = "/moneyChangeList")
    public FebsResponse moneyChangeList() {
        List<DataDictionaryCustom> list = dataDictionaryCustomMapper.selectDicByType("MONEY_CHANGE");
src/main/java/cc/mrbird/febs/mall/controller/ViewSystemController.java
@@ -188,4 +188,16 @@
    public String moneyChange() {
        return FebsUtil.view("modules/system/moneyChange");
    }
    @GetMapping("payLink")
    @RequiresPermissions("payLink:update")
    public String payLink(Model model) {
        PayLinkDto dto = new PayLinkDto();
        DataDictionaryCustom xtLink = dataDictionaryCustomMapper.selectDicDataByTypeAndCode("PAY_LINK", "XT_LINK");
        DataDictionaryCustom xtLinkImg = dataDictionaryCustomMapper.selectDicDataByTypeAndCode("PAY_LINK", "XT_LINK_IMG");
        if (xtLink != null) dto.setXtLink(xtLink.getValue());
        if (xtLinkImg != null) dto.setXtLinkImg(xtLinkImg.getValue());
        model.addAttribute("payLink", dto);
        return FebsUtil.view("modules/system/payLink");
    }
}
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/ApiMallOrderController.java
@@ -176,6 +176,49 @@
        return new FebsResponse().success().data(iXcxPayService.getTemplateId());
    }
    // ==================== XT 支付 ====================
    /**
     * 创建订单并通过 XT 支付
     * <p>
     * 流程:创建订单 → 通过 XT 支付链接
     * <p>
     */
    @ApiOperation(value = "创建订单-XT支付", notes = "创建订单并返回XT支付")
    @PostMapping(value = "/createOrderByXtPay")
    @Limit(key = "createOrderByXtPay", period = 1, count = 1, name = "XT下单", prefix = "limit", limitType = LimitType.IP)
    public FebsResponse createOrderByXtPay(@RequestBody @Validated XtPayCreateOrderDto lwPayDto) {
        // 1. 创建订单
        AddOrderDto addOrderDto = lwPayDto.getOrder();
        Long orderId = mallOrderInfoService.createOrder(addOrderDto);
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order != null
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
            return mallOrderInfoService.createOrderByXtPay();
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    @ApiOperation(value = "XT支付", notes = "XT支付")
    @PostMapping(value = "/payOrderByXtPay", produces = "application/json")
    public FebsResponse payOrderByXtPay(@RequestBody @Validated ApiOrderPayDto payDto) {
        Long orderId = payDto.getOrderId();
        Integer payType = payDto.getPayType();
        // 2. 获取订单详情
        MallOrderInfo order = mallOrderInfoService.getById(orderId);
        if (order != null
                && OrderConstants.PAY_TYPE_XT == payType
                && OrderStatusEnum.WAIT_PAY.getValue() == order.getStatus()
        ){
            return mallOrderInfoService.createOrderByXtPay();
        }
        return new FebsResponse().fail().message("Payment channel exception");
    }
    // ==================== LWPAY 支付 ====================
    /**
@@ -260,7 +303,7 @@
                result.put("orderNo", order.getOrderNo());
                result.put("amount", order.getAmount());
                result.put("payUrl", payUrl);
                return new FebsResponse().success().data(result).message("下单成功");
                return new FebsResponse().success().data(result).message("success");
            } catch (Exception e) {
                log.error("LWPAY 代收下单失败: orderId={}", orderId, e);
                return new FebsResponse().fail().message("Payment channel exception: " + e.getMessage());
src/main/java/cc/mrbird/febs/mall/controller/dependentStation/constant/OrderConstants.java
@@ -29,12 +29,13 @@
    public static final String ORDER_STATE = "order_state";
    /**
     * 支付方式 1-微信 2-支付宝 3-USDT
     * 支付方式 1-微信 2-支付宝 3-USDT 4-LWPAY支付 5-XT支付
     */
    public static final Integer PAY_TYPE_WECHAT = 1;
    public static final Integer PAY_TYPE_ALIPAY = 2;
    public static final Integer PAY_TYPE_USDT = 3;
    public static final Integer PAY_TYPE_SYSTEM = 4;
    public static final Integer PAY_TYPE_XT = 5;
    public static final String ORDER_PAY_TYPE = "order_pay_type";
    public static final String TRC20_SYSTEM_ADDRESS = "pay.trc20.address";
src/main/java/cc/mrbird/febs/mall/dto/ApiOrderPayDto.java
@@ -17,6 +17,6 @@
    private Long orderId;
    @NotNull(message = "支付方式不能为空")
    @ApiModelProperty(value = "支付方式 1-微信 2-支付宝 3-USDT 4-LwPAY", example = "you_ke_*****")
    @ApiModelProperty(value = "支付方式 1-微信 2-支付宝 3-USDT 4-LwPAY 5-XTPAY", example = "you_ke_*****")
    private Integer payType;
}
src/main/java/cc/mrbird/febs/mall/dto/PayLinkDto.java
New file
@@ -0,0 +1,17 @@
package cc.mrbird.febs.mall.dto;
import lombok.Data;
/**
 * 付款链接配置 DTO
 * 对应 data_dictionary_custom 表 type='PAY_LINK'
 *
 * @author auto-generated
 */
@Data
public class PayLinkDto {
    /** 付款链接 */
    private String xtLink;
    /** 付款链接图片 */
    private String xtLinkImg;
}
src/main/java/cc/mrbird/febs/mall/dto/RegisterDto.java
@@ -14,21 +14,21 @@
@ApiModel(value = "RegisterDto", description = "用户注册参数接收类")
public class RegisterDto {
    @NotBlank(message = "The First Name cannot be empty")
    @ApiModelProperty(value = "First Name", example = "15773001234")
    private String firstName;
    @NotBlank(message = "The LastName cannot be empty")
    @ApiModelProperty(value = "LastName", example = "15773001234")
    private String lastName;
//    @NotBlank(message = "The First Name cannot be empty")
//    @ApiModelProperty(value = "First Name", example = "15773001234")
//    private String firstName;
//
//    @NotBlank(message = "The LastName cannot be empty")
//    @ApiModelProperty(value = "LastName", example = "15773001234")
//    private String lastName;
    @NotBlank(message = "The Email Address cannot be empty")
    @ApiModelProperty(value = "Email Address", example = "15773001234")
    private String account;
    @NotBlank(message = "The Phone Number cannot be empty")
    @ApiModelProperty(value = "Phone Number", example = "15773001234")
    private String phoneNumber;
//
//    @NotBlank(message = "The Phone Number cannot be empty")
//    @ApiModelProperty(value = "Phone Number", example = "15773001234")
//    private String phoneNumber;
    @NotBlank(message = "The password cannot be empty")
    @ApiModelProperty(value = "Password", example = "123456")
src/main/java/cc/mrbird/febs/mall/dto/XtPayCreateOrderDto.java
New file
@@ -0,0 +1,18 @@
package cc.mrbird.febs.mall.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
@Data
@ApiModel(value = "XtPayCreateOrderDto", description = "支付下单参数")
public class XtPayCreateOrderDto {
    @Valid
    @NotNull(message = "订单信息不能为空")
    @ApiModelProperty(value = "订单信息", required = true)
    private AddOrderDto order;
}
src/main/java/cc/mrbird/febs/mall/service/IAdminMallOrderService.java
@@ -80,4 +80,6 @@
    FebsResponse deliverPdfGoods(Long id);
    FebsResponse cancelDeliver(Long id);
    FebsResponse insurePay(Long id);
}
src/main/java/cc/mrbird/febs/mall/service/IApiMallOrderInfoService.java
@@ -48,4 +48,6 @@
    FebsResponse createOrderVerify(ApiCreateOrderVerifyDto apiCreateOrderVerifyDto);
    FebsResponse chooseCoupon(ApiChooseCouponDto chooseCouponDto);
    FebsResponse createOrderByXtPay();
}
src/main/java/cc/mrbird/febs/mall/service/impl/AdminMallOrderService.java
@@ -5,6 +5,7 @@
import cc.mrbird.febs.common.enumerates.*;
import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.utils.ValidateEntityUtils;
import cc.mrbird.febs.mall.controller.dependentStation.constant.OrderConstants;
import cc.mrbird.febs.mall.dto.*;
import cc.mrbird.febs.mall.entity.*;
import cc.mrbird.febs.mall.mapper.*;
@@ -16,6 +17,7 @@
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.best.javaSdk.ClientParamEnum;
@@ -31,6 +33,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Slf4j
@@ -617,6 +620,33 @@
    }
    @Override
    public FebsResponse insurePay(Long id) {
        // 1. 查询订单
        MallOrderInfo order = mallOrderInfoMapper.selectById(id);
        if (order == null) {
            return new FebsResponse().fail().message("订单不存在,刷新后重试");
        }
        // 2. 幂等检查(避免重复回调)
        if (OrderStatusEnum.WAIT_PAY.getValue() != order.getStatus()) {
            return new FebsResponse().fail().message("订单不是代付款状态");
        }
        // 3. 更新订单状态为待发货
        mallOrderInfoMapper.update(
                null,
                Wrappers.lambdaUpdate(MallOrderInfo.class)
                        .set(MallOrderInfo::getStatus, OrderStatusEnum.WAIT_SHIPPING.getValue())
                        .set(MallOrderInfo::getPayTime, new Date())
                        .set(MallOrderInfo::getPayMethod, "XT支付")
                        .set(MallOrderInfo::getPayResult, "1")
                        .eq(MallOrderInfo::getId, order.getId())
        );
        return new FebsResponse().success();
    }
    @Override
    public void deliverGoodsByOrderNo(DeliverGoodsDto deliverGoodsDto) {
        MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectByOrderNo(deliverGoodsDto.getOrderNo());
        if (mallOrderInfo == null) {
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallMemberServiceImpl.java
@@ -94,8 +94,7 @@
            throw new FebsException("The two passwords are inconsistent");
        }
        mallMember = new MallMember();
        mallMember.setName(registerDto.getLastName() + registerDto.getFirstName());
        mallMember.setPhone(registerDto.getPhoneNumber());
        mallMember.setName(UUID.randomUUID().toString());
        mallMember.setEmail(registerDto.getAccount());
        mallMember.setPassword(SecureUtil.md5(password));
        mallMember.setAccountStatus(MallMember.ACCOUNT_STATUS_ENABLE);
@@ -104,9 +103,6 @@
        MallAddressInfo mallAddressInfo = new MallAddressInfo();
        mallAddressInfo.setMemberId(mallMember.getId());
        mallAddressInfo.setName(registerDto.getLastName());
        mallAddressInfo.setFristName(registerDto.getFirstName());
        mallAddressInfo.setPhone(registerDto.getPhoneNumber());
        mallAddressInfoMapper.insert(mallAddressInfo);
src/main/java/cc/mrbird/febs/mall/service/impl/ApiMallOrderInfoServiceImpl.java
@@ -1002,4 +1002,33 @@
        return new FebsResponse().success().data(objects);
    }
    @Override
    public FebsResponse createOrderByXtPay() {
        DataDictionaryCustom dataDictionaryCustom = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
                "PAY_LINK", "XT_LINK"
        );
        DataDictionaryCustom dataDictionaryCustomImg = dataDictionaryCustomMapper.selectDicDataByTypeAndCode(
                "PAY_LINK", "XT_LINK_IMG"
        );
        if (ObjectUtil.isEmpty(dataDictionaryCustom)){
            return new FebsResponse().fail().message("Payment channel exception");
        }
        if (ObjectUtil.isEmpty(dataDictionaryCustomImg)){
            return new FebsResponse().fail().message("Payment channel exception");
        }
        if (StrUtil.isEmpty(dataDictionaryCustom.getValue())){
            return new FebsResponse().fail().message("Payment channel exception");
        }
        if (StrUtil.isEmpty(dataDictionaryCustomImg.getValue())){
            return new FebsResponse().fail().message("Payment channel exception");
        }
        Map<String, Object> result = new HashMap<>();
        result.put("link", dataDictionaryCustom.getValue());
        result.put("linkImg", dataDictionaryCustomImg.getValue());
        return new FebsResponse().success().data(result);
    }
}
src/main/resources/templates/febs/views/modules/order/orderList.html
@@ -357,6 +357,11 @@
                    cancelOrder(data.id);
                });
            }
            if (layEvent === 'insurePay') {
                febs.modal.confirm('收款', '确认收款?', function () {
                    insurePay(data.id);
                });
            }
            if (layEvent === 'deliverPdfGoods') {
                febs.modal.confirm('一键发货', '确认一键发货?', function (index) {
                    layer.close(index);
@@ -396,6 +401,13 @@
        function cancelOrder(id) {
            febs.get(ctx + 'admin/order/cancelOrder/' + id, null, function () {
                febs.alert.success('操作成功');
                $query.click();
            });
        }
        function insurePay(id) {
            febs.get(ctx + 'admin/order/insurePay/' + id, null, function () {
                febs.alert.success('操作成功');
                $query.click();
            });
@@ -682,7 +694,9 @@
                        // {field: 'wxOrderNo', title: '支付订单号', minWidth: 120,align:'left'},
                        {title: '操作',
                            templet: function (d) {
                                if(d.status === 2){
                                if(d.status === 1){
                                    return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="insurePay" shiro:hasPermission="user:update">确认收款</button>'
                                }else if(d.status === 2){
                                    return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="deliverGoods" shiro:hasPermission="user:update">发货</button>'
                                    +'<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="seeOrder" shiro:hasPermission="user:update">详情</button>'
                                    // +''
src/main/resources/templates/febs/views/modules/system/payLink.html
New file
@@ -0,0 +1,105 @@
<div class="layui-fluid layui-anim febs-anim" id="pay-link" lay-title="付款链接设置">
    <div class="layui-row layui-col-space8 febs-container">
        <form class="layui-form" action="" lay-filter="pay-link-form">
            <div class="layui-card">
                <div class="layui-card-header">付款链接配置</div>
                <div class="layui-card-body">
                    <div class="layui-form-item">
                        <label class="layui-form-label required">付款链接:</label>
                        <div class="layui-input-block">
                            <input type="text" name="xtLink" data-th-id="${payLink.xtLink}"
                                   lay-verify="required" autocomplete="off" class="layui-input" placeholder="请输入付款链接地址">
                        </div>
                        <div class="layui-word-aux" style="margin-left: 150px;">用户点击付款时跳转的链接地址,必须以 http(s):// 开头</div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label required">链接图片:</label>
                        <div class="layui-upload">
                            <button type="button" class="layui-btn layui-btn-normal" id="pay-link-img-upload">上传图片</button>
                            <div class="layui-input-block">
                                <div class="layui-upload-list">
                                    <img class="layui-upload-img" id="xtLinkImgUrls" style="width: 200px;">
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label required">图片链接:</label>
                        <div class="layui-input-block">
                            <input type="text" id="xtLinkImg" name="xtLinkImg"
                                   autocomplete="off" class="layui-input" readonly>
                        </div>
                    </div>
                </div>
                <div class="layui-card-footer">
                    <button class="layui-btn layui-btn-normal" lay-submit="" lay-filter="pay-link-form-submit" id="submit">保存</button>
                </div>
            </div>
        </form>
    </div>
</div>
<style>
    .layui-form-label {
        width: 120px;
    }
    .layui-form-item .layui-input-block {
        margin-left: 150px;
    }
    .layui-table-form .layui-form-item {
        margin-bottom: 20px !important;
    }
    .layui-upload-list {
        margin: 0 !important;
    }
</style>
<script data-th-inline="javascript" type="text/javascript">
    layui.use(['dropdown', 'jquery', 'validate', 'febs', 'form', 'upload'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            form = layui.form,
            upload = layui.upload,
            payLink = [[${payLink}]],
            validate = layui.validate,
            $view = $('#pay-link');
        form.verify(validate);
        if (payLink) {
            form.val("pay-link-form", {
                "xtLink": payLink.xtLink,
                "xtLinkImg": payLink.xtLinkImg
            });
            if (payLink.xtLinkImg) {
                $('#xtLinkImgUrls').attr('src', payLink.xtLinkImg);
            }
        }
        form.render();
        // 图片上传
        upload.render({
            elem: '#pay-link-img-upload'
            ,url: ctx + 'admin/goods/uploadFileBase64'
            ,before: function(obj){
                obj.preview(function(index, file, result){
                    $('#xtLinkImgUrls').attr('src', result);
                });
            }
            ,done: function(res){
                febs.alert.success(res.data.src);
                $("#xtLinkImg").val(res.data.src);
            }
        });
        form.on('submit(pay-link-form-submit)', function (data) {
            febs.post(ctx + 'admin/system/payLink', data.field, function (res) {
                febs.alert.success('保存成功');
            });
            return false;
        });
    });
</script>