admin
2024-07-29 734dfe9eb0a2176103dce8245c69b1194574c68e
代理新功能完善
26个文件已修改
4个文件已添加
1076 ■■■■ 已修改文件
src/main/java/com/taoke/autopay/controller/admin/AdminAgentSettleController.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/AdminSettingsController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/AdminWxUserController.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/agent/AgentController.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/client/OrderController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/dao/WxUserInfoMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/dao/agent/ChannelAgentSettleRecordMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/dto/admin/AgentSettleExcelDataDto.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/entity/SystemConfigKeyEnum.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/entity/agent/ChannelAgentOrderStatisticRecord.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/factory/OrderFactory.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/manager/OrderPayFailProcessor.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/agent/ChannelAgentSettleService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/impl/KeyOrderServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/impl/agent/ChannelAgentSettleServiceImpl.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/utils/AgentUtil.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/utils/order/DYOrderApi.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/vo/AgentWithdrawVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/vo/admin/AgentSearchVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ChannelAgentSettleRecordMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/KeyOrderMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/WxUserInfoMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/agent-settle-list.html 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/agent-withdraw-list.html 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/index.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/agent/css/index.css 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/agent/img/icon_input_miandan.png 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/agent/img/icon_miandan.png 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/agent/index.html 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/agent/js/index.js 98 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/AdminAgentSettleController.java
@@ -1,12 +1,18 @@
package com.taoke.autopay.controller.admin;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.dao.agent.ChannelAgentMapper;
import com.taoke.autopay.dao.agent.ChannelAgentSettleRecordMapper;
import com.taoke.autopay.dto.admin.AgentSettleExcelDataDto;
import com.taoke.autopay.dto.admin.OrderExcelDataDto;
import com.taoke.autopay.entity.OrderChannelEnum;
import com.taoke.autopay.entity.SystemConfigKeyEnum;
import com.taoke.autopay.entity.agent.ChannelAgent;
@@ -21,20 +27,30 @@
import com.taoke.autopay.service.agent.ChannelAgentSettingService;
import com.taoke.autopay.service.agent.ChannelAgentSettleService;
import com.taoke.autopay.service.agent.ChannelAgentSharingRatioService;
import com.taoke.autopay.utils.Constant;
import com.taoke.autopay.utils.TimeUtil;
import com.taoke.autopay.vo.admin.AdminChannelAgentVO;
import com.taoke.autopay.vo.admin.AgentSearchVO;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.yeshi.utils.JsonUtil;
import org.yeshi.utils.NumberUtil;
import org.yeshi.utils.StringUtil;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
/**
@@ -60,6 +76,20 @@
        @Override
        public Date read(JsonReader in) throws IOException {
            return new Date();
        }
    }).registerTypeAdapter(BigDecimal.class, new TypeAdapter<BigDecimal>() {
        @Override
        public void write(JsonWriter out, BigDecimal value) throws IOException {
            if (value != null) {
                out.value(value.toString());
            } else {
                out.value("");
            }
        }
        @Override
        public BigDecimal read(JsonReader in) throws IOException {
            return new BigDecimal("0.00");
        }
    }).create();
@@ -106,4 +136,136 @@
            return JsonUtil.loadFalseResult(e.getMessage());
        }
    }
    /**
     * @author hxh
     * @description 提现列表
     * @date 13:14 2024/7/29
     * @param: key
     * @param: startTime
     * @param: endTime
     * @param: status
     * @param: page
     * @param: limit
     * @return java.lang.String
     **/
    @ResponseBody
    @RequestMapping("listWithdraw")
    public String listWithdraw(String key, String startTime,String endTime, String status, int page, int limit) {
        //先查询所有的数据
        ChannelAgentSettleRecordMapper.DaoQuery query = new ChannelAgentSettleRecordMapper.DaoQuery();
        query.agentId = !NumberUtil.isNumeric(key)?null:Long.parseLong(key);
        query.minWithDrawApplyTime = StringUtil.isNullOrEmpty(startTime)?null:new Date(TimeUtil.convertToTimeTemp(startTime,"yyyy-MM-dd HH:mm:ss"));
        query.maxWithDrawApplyTime = StringUtil.isNullOrEmpty(endTime)?null:new Date(TimeUtil.convertToTimeTemp(endTime,"yyyy-MM-dd HH:mm:ss"));
        if(status==null||Integer.parseInt(status)<0){
            query.statusList=Arrays.asList(new Integer[]{ ChannelAgentSettleRecord.STATUS_WITHDRAWING,ChannelAgentSettleRecord.STATUS_WITHDRAW_SUCCESS,ChannelAgentSettleRecord.STATUS_WITHDRAW_REJECTED});
        }else{
            query.status=Integer.parseInt(status);
        }
        query.sortList = Arrays.asList(new String[]{"_withdraw_apply_time desc"});
        query.start = (long) (page - 1) * limit;
        query.count = limit;
        List<ChannelAgentSettleRecord> recordList = channelAgentSettleService.list(query);
        long count = channelAgentSettleService.count(query);
        JSONObject data = new JSONObject();
        data.put("count", count);
        data.put("list", gson.toJson(recordList));
        return JsonUtil.loadTrueResult(data);
    }
    /**
     * @author hxh
     * @description
     * @date 13:23 2024/7/29
     * @param: ids JSONARRAY
     * @return java.lang.String
     **/
    @ResponseBody
    @RequestMapping("pass")
    public String passWithdraw(String ids) {
       if(StringUtil.isNullOrEmpty(ids)){
           return JsonUtil.loadFalseResult("请上传ids");
       }
        Type type=new TypeToken<List<Long>>(){}.getType();
        try {
            channelAgentSettleService.processWithdraw(gson.fromJson(ids,type), true);
            return JsonUtil.loadTrueResult("");
        } catch (ChannelAgentSettleException e) {
            return JsonUtil.loadFalseResult(e.getMessage());
        }
    }
    @ResponseBody
    @RequestMapping("reject")
    public String rejectWithdraw(String ids) {
        if(StringUtil.isNullOrEmpty(ids)){
            return JsonUtil.loadFalseResult("请上传ids");
        }
        Type type=new TypeToken<List<Long>>(){}.getType();
        try {
            channelAgentSettleService.processWithdraw(gson.fromJson(ids,type), false);
            return JsonUtil.loadTrueResult("");
        } catch (ChannelAgentSettleException e) {
            return JsonUtil.loadFalseResult(e.getMessage());
        }
    }
    @RequestMapping("downloadSettleTable")
    public void downloadSettleTable(String day, HttpServletResponse response) throws IOException {
        ChannelAgentSettleRecordMapper.DaoQuery daoQuery=new ChannelAgentSettleRecordMapper.DaoQuery();
        daoQuery.settleDay = day;
        daoQuery.count = 10000;
        daoQuery.status =  ChannelAgentSettleRecord.STATUS_NOT_SETTLE;
        List<ChannelAgentSettleRecord> list =   channelAgentSettleService.list(daoQuery);
        List<AgentSettleExcelDataDto> dataList=new ArrayList<>();
        for(ChannelAgentSettleRecord record:list){
            AgentSettleExcelDataDto dto=AgentSettleExcelDataDto.builder()
                    .id(record.getId())
                    .agentId(record.getAgentId())
                    .actualSettleMoney(record.getSettleMoney().toString())
                    .settleMoney(record.getSettleMoney().toString())
                    .day(record.getSettleDay())
                    .build();
            dataList.add(dto);
        }
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName="settle_"+URLEncoder.encode(TimeUtil.getGernalTime(System.currentTimeMillis(),"yyyyMMdd_HHmmss"),"UTF-8");
        response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");
        EasyExcel.write(response.getOutputStream(),AgentSettleExcelDataDto.class).sheet("结算确认单").doWrite(dataList);
    }
    @ResponseBody
    @RequestMapping("uploadSettleExcel")
    public String uploadImg(@RequestParam("file") MultipartFile file, HttpSession session) throws IOException {
        InputStream inputStream = file.getInputStream();
        EasyExcel.read(inputStream, AgentSettleExcelDataDto.class, new ReadListener<AgentSettleExcelDataDto>() {
            @Override
            public void invoke(AgentSettleExcelDataDto o, AnalysisContext analysisContext) {
                System.out.println("读取到数据: " + o.getDay());
                try {
                    channelAgentSettleService.actualSettle(o.getId(),new BigDecimal(o.getActualSettleMoney()));
                } catch (ChannelAgentSettleException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            }
        }).sheet().doRead();
        return JsonUtil.loadTrueResult("");
    }
}
src/main/java/com/taoke/autopay/controller/admin/AdminSettingsController.java
@@ -9,19 +9,15 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.yeshi.utils.BigDecimalUtil;
import org.yeshi.utils.JsonUtil;
import org.yeshi.utils.StringUtil;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import static java.math.BigDecimal.ROUND_FLOOR;
@Controller
@RequestMapping("/admin/api/settings")
src/main/java/com/taoke/autopay/controller/admin/AdminWxUserController.java
@@ -67,8 +67,12 @@
        //先查询所有的数据
        WxUserInfoMapper.DaoQuery query = new WxUserInfoMapper.DaoQuery();
        query.sortList=Arrays.asList(new String[]{"login_time desc"});
        if (!StringUtil.isNullOrEmpty(uid) && NumberUtil.isNumeric(uid)) {
            query.id = Long.parseLong(uid);
        if (!StringUtil.isNullOrEmpty(uid)) {
            if(NumberUtil.isNumeric(uid)) {
                query.id = Long.parseLong(uid);
            }else{
                query.searchKey = uid;
            }
        }
        List<WxUserInfo> userList = wxUserService.list(query, page, limit);
        long count = wxUserService.count(query);
src/main/java/com/taoke/autopay/controller/agent/AgentController.java
@@ -6,31 +6,29 @@
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.dao.KeyOrderMapper;
import com.taoke.autopay.dao.agent.ChannelAgentMapper;
import com.taoke.autopay.dao.agent.ChannelAgentSettleRecordMapper;
import com.taoke.autopay.dto.ChannelOrderStatistic;
import com.taoke.autopay.entity.KeyOrder;
import com.taoke.autopay.entity.OrderChannelEnum;
import com.taoke.autopay.entity.SystemConfigKeyEnum;
import com.taoke.autopay.entity.agent.ChannelAgent;
import com.taoke.autopay.entity.agent.ChannelAgentSettings;
import com.taoke.autopay.entity.agent.ChannelAgentSharingRatio;
import com.taoke.autopay.entity.agent.ChannelAgentSettleDetail;
import com.taoke.autopay.entity.agent.ChannelAgentSettleRecord;
import com.taoke.autopay.exception.ChannelAgentException;
import com.taoke.autopay.factory.AgentFactory;
import com.taoke.autopay.exception.ChannelAgentSettleException;
import com.taoke.autopay.factory.OrderFactory;
import com.taoke.autopay.service.KeyOrderService;
import com.taoke.autopay.service.SystemConfigService;
import com.taoke.autopay.service.agent.ChannelAgentService;
import com.taoke.autopay.service.agent.ChannelAgentSettingService;
import com.taoke.autopay.service.agent.ChannelAgentSettleService;
import com.taoke.autopay.service.agent.ChannelAgentSharingRatioService;
import com.taoke.autopay.utils.AgentUtil;
import com.taoke.autopay.utils.Constant;
import com.taoke.autopay.utils.StringUtil;
import com.taoke.autopay.utils.TimeUtil;
import com.taoke.autopay.vo.AgentOrderFilter;
import com.taoke.autopay.vo.AgentOrderVO;
import com.taoke.autopay.vo.OrderFilter;
import com.taoke.autopay.vo.admin.AdminChannelAgentVO;
import com.taoke.autopay.vo.admin.AgentSearchVO;
import net.sf.json.JSONArray;
import com.taoke.autopay.vo.AgentWithdrawVO;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -75,6 +73,12 @@
    @Resource
    private ChannelAgentSharingRatioService channelAgentSharingRatioService;
    @Resource
    private ChannelAgentSettleService channelAgentSettleService;
    @Resource
    private SystemConfigService systemConfigService;
    @ResponseBody
@@ -176,4 +180,96 @@
    }
    @ResponseBody
    @RequestMapping("getConfig")
    public String getConfig(HttpSession session) {
        ChannelAgent agent = (ChannelAgent) session.getAttribute(Constant.SESSION_KEY_AGENT);
        if (agent == null) {
            return JsonUtil.loadFalseResult("尚未登录");
        }
        String baseSubmitKeyLink = systemConfigService.getValueCache(SystemConfigKeyEnum.AGENT_SUBMIT_KEY_LINK);
        String submitKeyUrl = AgentUtil.getSubmitKeyUrl(baseSubmitKeyLink, agent.getAlias());
        JSONObject data = new JSONObject();
        data.put("submitKeyUrl", submitKeyUrl);
        return JsonUtil.loadTrueResult(data);
    }
    @ResponseBody
    @RequestMapping("withdrawList")
    public String withdrawList(int page, HttpSession session) {
        ChannelAgent agent = (ChannelAgent) session.getAttribute(Constant.SESSION_KEY_AGENT);
        if (agent == null) {
            return JsonUtil.loadFalseResult("尚未登录");
        }
        ChannelAgentSettleRecordMapper.DaoQuery daoQuery = new ChannelAgentSettleRecordMapper.DaoQuery();
        daoQuery.statusList = Arrays.asList(new Integer[]{ChannelAgentSettleRecord.STATUS_SETTLED, ChannelAgentSettleRecord.STATUS_WITHDRAWING, ChannelAgentSettleRecord.STATUS_WITHDRAW_SUCCESS, ChannelAgentSettleRecord.STATUS_WITHDRAW_REJECTED});
        daoQuery.sortList = Arrays.asList(new String[]{"_update_time desc"});
        daoQuery.count = 20;
        daoQuery.start = (page - 1) * 20;
        List<ChannelAgentSettleRecord> recordList = channelAgentSettleService.list(daoQuery);
        long count = channelAgentSettleService.count(daoQuery);
        List<AgentWithdrawVO> voList = new ArrayList<>();
        for (ChannelAgentSettleRecord record : recordList) {
            long totalOrderCount = 0;
            for (ChannelAgentSettleDetail d : record.getDetailList()) {
                totalOrderCount += d.getPayOrderCount();
            }
            String statusDesc = "";
            switch (record.getStatus()) {
                case ChannelAgentSettleRecord.STATUS_SETTLED:
                    statusDesc = "未提现";
                    break;
                case ChannelAgentSettleRecord.STATUS_WITHDRAWING:
                    statusDesc = "正在审核";
                    break;
                case ChannelAgentSettleRecord.STATUS_WITHDRAW_SUCCESS:
                    statusDesc = "提现成功";
                    break;
                case ChannelAgentSettleRecord.STATUS_WITHDRAW_REJECTED:
                    statusDesc = "提现失败";
                    break;
            }
            voList.add(AgentWithdrawVO.builder()
                    .id(record.getId())
                    .money(record.getActualSettleMoney().toString())
                    .month(TimeUtil.getGernalTime(record.getSettleTime().getTime(), "yyyy年MM月"))
                    .status(record.getStatus())
                    .statusDesc(statusDesc)
                    .orderCount(totalOrderCount)
                    .settleTime(TimeUtil.getGernalTime(record.getSettleTime().getTime(), "yyyy.MM.dd HH:mm:ss"))
                    .build());
        }
        JSONObject data = new JSONObject();
        data.put("list", voList);
        data.put("count", count);
        return JsonUtil.loadTrueResult(data);
    }
    @ResponseBody
    @RequestMapping("withdraw")
    public String withdraw(Long id, HttpSession session) {
        ChannelAgent agent = (ChannelAgent) session.getAttribute(Constant.SESSION_KEY_AGENT);
        if (agent == null) {
            return JsonUtil.loadFalseResult("尚未登录");
        }
        if(StringUtil.isNullOrEmpty(agent.getAlipayName())||StringUtil.isNullOrEmpty(agent.getAlipayAccount()))
        {
            return JsonUtil.loadFalseResult("尚未设置支付宝信息");
        }
        try {
            channelAgentSettleService.applyWithdraw(id);
            return JsonUtil.loadTrueResult("");
        } catch (ChannelAgentSettleException e) {
            e.printStackTrace();
            return JsonUtil.loadFalseResult("尚未登录");
        }
    }
}
src/main/java/com/taoke/autopay/controller/client/OrderController.java
@@ -291,7 +291,7 @@
        int orderType=Constant.ORDER_TYPE_UNKNOWN;
        if(orderNoDesc.contains("抖音")){
        if(orderNoDesc.contains("抖音")||orderNoDesc.contains("上海格物致品")){
            orderType = Constant.ORDER_TYPE_DY;
        }else  if(orderNoDesc.contains("快手")){
            orderType = Constant.ORDER_TYPE_KS;
src/main/java/com/taoke/autopay/dao/WxUserInfoMapper.java
@@ -21,6 +21,7 @@
        public Long id;
        public String openId;
        public String nickName;
        public String searchKey;
        public String portrait;
        public Date minLoginTime;
        public Date maxLoginTime;
src/main/java/com/taoke/autopay/dao/agent/ChannelAgentSettleRecordMapper.java
@@ -27,6 +27,7 @@
        public Date minPayTime;
        public Date maxPayTime;
        public Integer status;
        public List<Integer> statusList;
        public Integer statusDesc;
        public Date minSettleTime;
        public Date maxSettleTime;
src/main/java/com/taoke/autopay/dto/admin/AgentSettleExcelDataDto.java
New file
@@ -0,0 +1,34 @@
package com.taoke.autopay.dto.admin;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;
/**
 * @author hxh
 * @title: OrderExcelDataDto
 * @description: 渠道结算excel输出
 * @date 2024/7/18 22:44
 */
@Data
@Builder
public class AgentSettleExcelDataDto {
    @Tolerate
    public AgentSettleExcelDataDto() {
    }
    @ExcelProperty("ID")
    private Long id;
    @ExcelProperty("渠道ID")
    private Long agentId;
    @ExcelProperty("日期")
    private String day;
    @ExcelProperty("预结算金额")
    private String settleMoney;
    @ExcelProperty("实际结算金额")
    private String actualSettleMoney;
}
src/main/java/com/taoke/autopay/entity/SystemConfigKeyEnum.java
@@ -19,7 +19,9 @@
    PAY_MONEY_LIST("pay_money_list", "可支付金额列表"),
    ALIPAY_KEY_VERIFY("alipay_key_verify_state", "是否需要提前验证支付宝口令"),
    AGENT_SUBMIT_KEY_LINK("agent_submit_key_link", "代理口令提交链接"),
    AGENT_ADMIN_LINK("agent_admin_link", "代理后台管理链接"),
    AGENT_ORDER_CHANNEL_SHARE_RATIO("agent_order_channel_share_ratio", "代理订单渠道分成比例"),
    RE_EXCUTE_PAY_CLIENTS("re_excute_pay_clients", "重新执行支付的账号"),
    ;
src/main/java/com/taoke/autopay/entity/agent/ChannelAgentOrderStatisticRecord.java
@@ -1,5 +1,6 @@
package com.taoke.autopay.entity.agent;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;
import org.springframework.data.annotation.Id;
@@ -15,6 +16,7 @@
 * @description: 渠道订单支付记录
 * @date 2024/7/24 18:13
 */
@Builder
@Data
@Table(value="table_agent_order_statistic_record")
public class ChannelAgentOrderStatisticRecord {
src/main/java/com/taoke/autopay/factory/OrderFactory.java
@@ -54,7 +54,7 @@
        String time=TimeUtil.getGernalTime(order.getCreateTime().getTime(), "yyyy.MM.dd HH:mm:ss");
        time = time.replace(" ","<br>");
        return AgentOrderVO.builder()
                .payMoney(order.getOrderMoney().toString())
                .payMoney(order.getOrderMoney()!=null?order.getOrderMoney().toString():"未知")
                .settleMoney(money.toString())
                .createTime(time)
                .valid(order.getPayTime() != null && order.getState() == KeyOrder.STATE_PAY)
src/main/java/com/taoke/autopay/manager/OrderPayFailProcessor.java
@@ -1,7 +1,10 @@
package com.taoke.autopay.manager;
import com.taoke.autopay.entity.KeyOrder;
import com.taoke.autopay.entity.SystemConfigKeyEnum;
import com.taoke.autopay.service.KeyOrderService;
import com.taoke.autopay.service.SystemConfigService;
import com.taoke.autopay.utils.StringUtil;
import lombok.Builder;
import lombok.Data;
import org.springframework.stereotype.Component;
@@ -24,7 +27,7 @@
    @Data
    @Builder
    public static class OrderQueue implements Comparable{
    public static class OrderQueue implements Comparable {
        private String id;
        private long expireTime;
@@ -37,73 +40,88 @@
    @Resource
    private KeyOrderService keyOrderService;
    private Map<String,Integer> reProcessCountMap=new HashMap<>();
    private Map<String, Integer> reProcessCountMap = new HashMap<>();
    private Queue<OrderQueue> orderQueues=new PriorityQueue<>();
    private Queue<OrderQueue> orderQueues = new PriorityQueue<>();
    public void clearCacheData(){
    @Resource
    private SystemConfigService systemConfigService;
    public void clearCacheData() {
        reProcessCountMap.clear();
        orderQueues.clear();
    }
    @Transactional(rollbackFor = Exception.class)
    public void processFromQueue(){
        if(orderQueues.isEmpty()){
    public void processFromQueue() {
        if (orderQueues.isEmpty()) {
            return;
        }
        OrderQueue queue =  orderQueues.peek();
        if(queue!=null&&System.currentTimeMillis() > queue.expireTime){
            queue =  orderQueues.poll();
           KeyOrder order = keyOrderService.selectById(queue.getId());
           if(order==null||order.getState()!=KeyOrder.STATE_REJECT_PAY){
               return;
           }
            if(reProcessCountMap.containsKey(queue.getId())&&reProcessCountMap.get(queue.getId())>3){
        OrderQueue queue = orderQueues.peek();
        if (queue != null && System.currentTimeMillis() > queue.expireTime) {
            queue = orderQueues.poll();
            KeyOrder order = keyOrderService.selectById(queue.getId());
            if (order == null || order.getState() != KeyOrder.STATE_REJECT_PAY) {
                return;
            }
            if (reProcessCountMap.containsKey(queue.getId()) && reProcessCountMap.get(queue.getId()) > 3) {
                return;
            }
           if(!reProcessCountMap.containsKey(queue.getId())){
               reProcessCountMap.put(queue.getId(), 0);
           }
            reProcessCountMap.put(queue.getId(), reProcessCountMap.get(queue.getId())+1);
            if (!reProcessCountMap.containsKey(queue.getId())) {
                reProcessCountMap.put(queue.getId(), 0);
            }
            reProcessCountMap.put(queue.getId(), reProcessCountMap.get(queue.getId()) + 1);
            keyOrderService.removeDistributedClient(queue.getId());
            String clientIds = systemConfigService.getValueCache(SystemConfigKeyEnum.RE_EXCUTE_PAY_CLIENTS);
            // 移除已经分配的设备,改变状态为未分配
            KeyOrder update=new KeyOrder();
            KeyOrder update = new KeyOrder();
            update.setId(queue.getId());
            update.setState(KeyOrder.STATE_NOT_PROCESS);
            update.setStateDesc("重新分配");
            if (!StringUtil.isNullOrEmpty(clientIds)) {
                String[] cids = clientIds.split(",");
                int index = (int) Math.round(Math.random() * cids.length);
                if (index + 1 > cids.length) {
                    index = cids.length - 1;
                }
                update.setDistributeClientUid(Long.parseLong(cids[index]));
            }
            keyOrderService.update(update);
        }
    }
    /**
     * @return void
     * @author hxh
     * @description 处理支付失败
     * @date 21:28 2024/7/26
     * @param: id
     * @param: msg
     * @return void
     **/
    @Transactional(rollbackFor = Exception.class)
    public void processPayFail(String id,String msg){
        if(msg==null||!msg.contains("超时")){
    public void processPayFail(String id, String msg) {
        if (msg == null || !msg.contains("超时")) {
            return;
        }
        //加入重试队列
        orderQueues.add(OrderQueue.builder().id(id).expireTime(System.currentTimeMillis()+2*60*1000).build());
        KeyOrder update=new KeyOrder();
        orderQueues.add(OrderQueue.builder().id(id).expireTime(System.currentTimeMillis() + 2 * 60 * 1000).build());
        KeyOrder update = new KeyOrder();
        update.setId(id);
        update.setState(KeyOrder.STATE_REJECT_PAY);
        update.setStateDesc(msg);
        keyOrderService.update(update);
    }
    public static void main(String[] args){
        Queue<OrderQueue> orderQueues=new PriorityQueue<>();
    public static void main(String[] args) {
        Queue<OrderQueue> orderQueues = new PriorityQueue<>();
        orderQueues.add(OrderQueue.builder().id("1").build());
        orderQueues.add(OrderQueue.builder().id("2").build());
        OrderQueue queue =  orderQueues.peek();
        queue =  orderQueues.poll();
        OrderQueue queue = orderQueues.peek();
        queue = orderQueues.poll();
        orderQueues.isEmpty();
src/main/java/com/taoke/autopay/service/agent/ChannelAgentSettleService.java
@@ -4,6 +4,7 @@
import com.taoke.autopay.entity.agent.ChannelAgentSettleRecord;
import com.taoke.autopay.exception.ChannelAgentSettleException;
import java.math.BigDecimal;
import java.util.List;
public interface ChannelAgentSettleService {
@@ -22,6 +23,28 @@
    public void delete(Long id);
    public void processWithdraw(List<Long> ids, boolean pass) throws ChannelAgentSettleException;
    /**
     * @author hxh
     * @description 实际结算
     * @date 19:36 2024/7/29
     * @param: id
     * @param: money
     * @return void
     **/
    public void actualSettle(Long id, BigDecimal money) throws ChannelAgentSettleException;
    /**
     * @author hxh
     * @description /申请提现
     * @date 19:57 2024/7/29
     * @param: id
     * @return void
     **/
    public void applyWithdraw(Long id) throws ChannelAgentSettleException;
}
src/main/java/com/taoke/autopay/service/impl/KeyOrderServiceImpl.java
@@ -280,7 +280,7 @@
    @Override
    public DYOrderDto verifyKey(String orderNoDesc, String orderStatus, String money) throws KeyVerifyException {
        int orderType = Constant.ORDER_TYPE_UNKNOWN;
        if (orderNoDesc.contains("抖音")) {
        if (orderNoDesc.contains("抖音")||orderNoDesc.contains("上海格物致品")) {
            orderType = Constant.ORDER_TYPE_DY;
        } else if (orderNoDesc.contains("快手")) {
            orderType = Constant.ORDER_TYPE_KS;
src/main/java/com/taoke/autopay/service/impl/agent/ChannelAgentSettleServiceImpl.java
@@ -164,4 +164,63 @@
        }
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void processWithdraw(List<Long> ids, boolean pass) throws ChannelAgentSettleException {
        for (Long id : ids) {
            ChannelAgentSettleRecord old = channelAgentSettleRecordMapper.selectByPrimaryKeyForUpdate(id);
            if (old.getStatus() != ChannelAgentSettleRecord.STATUS_WITHDRAWING) {
                throw new ChannelAgentSettleException("提现已处理");
            }
            channelAgentSettleRecordMapper.updateByPrimaryKeySelective(ChannelAgentSettleRecord.builder().id(id)
                    .status(pass ? ChannelAgentSettleRecord.STATUS_WITHDRAW_SUCCESS : ChannelAgentSettleRecord.STATUS_WITHDRAW_REJECTED)
                    .withDrawProcessTime(new Date())
                    .updateTime(new Date())
                    .payTime(pass ? new Date() : null).build());
        }
    }
    @Override
    public void actualSettle(Long id, BigDecimal money) throws ChannelAgentSettleException {
        ChannelAgentSettleRecord old = channelAgentSettleRecordMapper.selectByPrimaryKeyForUpdate(id);
        if (old == null) {
            throw new ChannelAgentSettleException("结算ID为空");
        }
        if (old.getStatus() != ChannelAgentSettleRecord.STATUS_NOT_SETTLE) {
            throw new ChannelAgentSettleException("已经结算");
        }
        channelAgentSettleRecordMapper.updateByPrimaryKeySelective(ChannelAgentSettleRecord.builder()
                .id(id)
                .actualSettleMoney(money)
                .settleTime(new Date())
                .status(ChannelAgentSettleRecord.STATUS_SETTLED)
                .updateTime(new Date())
                .build());
    }
    @Override
    public void applyWithdraw(Long id) throws ChannelAgentSettleException {
        ChannelAgentSettleRecord old = channelAgentSettleRecordMapper.selectByPrimaryKeyForUpdate(id);
        if (old == null) {
            throw new ChannelAgentSettleException("结算ID为空");
        }
        if (old.getStatus() != ChannelAgentSettleRecord.STATUS_SETTLED) {
            throw new ChannelAgentSettleException("处于不可提现状态");
        }
        channelAgentSettleRecordMapper.updateByPrimaryKeySelective(ChannelAgentSettleRecord.builder()
                .id(id)
                .withDrawApplyTime(new Date())
                .settleTime(new Date())
                .status(ChannelAgentSettleRecord.STATUS_WITHDRAWING)
                .updateTime(new Date())
                .build());
    }
}
src/main/java/com/taoke/autopay/utils/AgentUtil.java
@@ -6,4 +6,8 @@
        return baseUrl.replace("{alias}",alias);
    }
    public static String getAgentAdminUrl(String baseUrl, String alias){
        return baseUrl.replace("{alias}",alias);
    }
}
src/main/java/com/taoke/autopay/utils/order/DYOrderApi.java
@@ -61,6 +61,7 @@
    private static DYOrderDto getOrderDetail2(String orderNo) throws KeyOrderException {
        String result = requestByOrderNo2(orderNo);
        System.out.println(result);
        JSONObject root = JSONObject.fromObject(result);
        if (root.optInt("errCode") != 0) {
            logger.error(String.format("抖音订单查询出错(2):%s - %s",orderNo, result));
@@ -131,7 +132,7 @@
    public static void main(String[] args) throws Exception {
//        DYOrderDto dto = (DYOrderApi.getOrderDetail("6932591080266339994"));
        DYOrderDto result = getOrderDetail("6932591080266471066");
        DYOrderDto result = getOrderDetail("6932676890890213137");
        System.out.println(result);
    }
src/main/java/com/taoke/autopay/vo/AgentWithdrawVO.java
New file
@@ -0,0 +1,22 @@
package com.taoke.autopay.vo;
import lombok.Builder;
import lombok.Data;
/**
 * @author hxh
 * @title: AgentWithdrawVO
 * @description: 代理提现
 * @date 2024/7/29 17:01
 */
@Data
@Builder
public class AgentWithdrawVO {
    private Long id;
    private String month;
    private String settleTime;
    private long orderCount;
    private String money;
    private int status;
    private String statusDesc;
}
src/main/java/com/taoke/autopay/vo/admin/AgentSearchVO.java
@@ -1,5 +1,6 @@
package com.taoke.autopay.vo.admin;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;
@@ -9,6 +10,7 @@
 * @description: 订单搜索
 * @date 2024/7/18 22:33
 */
@Builder
@Data
public class AgentSearchVO {
    @Tolerate
src/main/resources/mapper/ChannelAgentSettleRecordMapper.xml
@@ -48,6 +48,14 @@
        <if test="query.minPayTime!=null">AND _pay_time &gt;= #{query.minPayTime}</if>
        <if test="query.maxPayTime!=null">AND #{query.maxPayTime} &gt; _pay_time</if>
        <if test="query.status!=null">AND _status = #{query.status}</if>
        <if test="query.statusList!=null">
            <foreach collection="query.statusList" open=" AND (" close=")" separator=" or " item="status">
                _status = #{status}
            </foreach>
        </if>
        <if test="query.statusDesc!=null">AND _status_desc = #{query.statusDesc}</if>
        <if test="query.minSettleTime!=null">AND _settle_time &gt;= #{query.minSettleTime}</if>
        <if test="query.maxSettleTime!=null">AND #{query.maxSettleTime} &gt; _settle_time</if>
src/main/resources/mapper/KeyOrderMapper.xml
@@ -76,7 +76,7 @@
        <if test="query.oMaxCreateTime!=null">AND #{query.oMaxCreateTime} &gt; o.create_time</if>
        <if test="query.minUpdateTime!=null">AND update_time &gt;= #{query.minUpdateTime}</if>
        <if test="query.maxUpdateTime!=null">AND #{query.maxUpdateTime} &gt; update_time</if>
        <if test="query.nickName!=null">AND nick_name like '%${nickName}%' &gt; update_time</if>
        <if test="query.nickName!=null">AND u.nick_name like '%${query.nickName}%' </if>
    </sql>
    <select id="list" resultMap="BaseResultMap">select
        <include refid="Base_Column_List"/>
src/main/resources/mapper/WxUserInfoMapper.xml
@@ -22,6 +22,7 @@
    <if test="query.id!=null">AND id = #{query.id}</if>
    <if test="query.openId!=null">AND openid = #{query.openId}</if>
    <if test="query.nickName!=null">AND nick_name = #{query.nickName}</if>
    <if test="query.searchKey!=null">AND (nick_name like '%${query.searchKey}%' or openid = #{query.searchKey})</if>
    <if test="query.portrait!=null">AND portrait = #{query.portrait}</if>
    <if test="query.minLoginTime!=null">AND login_time >= #{query.minLoginTime}</if>
    <if test="query.maxLoginTime!=null">AND #{query.maxLoginTime} > login_time</if>
src/main/resources/static/admin/agent-settle-list.html
@@ -5,7 +5,8 @@
        <meta charset="UTF-8">
        <meta name="renderer" content="webkit">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <meta name="viewport"
            content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title>设备列表</title>
        <link rel="stylesheet" type="text/css" href="layui/css/layui.css" />
        <link rel="stylesheet" type="text/css" href="css/admin.css" />
@@ -22,13 +23,24 @@
                <div class="layui-form-item">
                    <div class="layui-inline">
                        <input type="text" name="key" id="key" placeholder="按渠道ID/名称搜索" autocomplete="off" class="layui-input">
                        <input type="text" name="key" id="key" placeholder="按渠道ID搜索" autocomplete="off"
                            class="layui-input">
                    </div>
                    <div class="layui-inline">
                        <input type="text" name="day" placeholder="日期" autocomplete="off" class="layui-input">
                    </div>
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i class="layui-icon layui-icon-search"></i>搜索</button>
                    <a href="javascript:void();" class="layui-btn layui-btn-warm" onclick="add_agent()"><i class="layui-icon layui-icon-add-circle"></i> 开始预结算</a>
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i
                            class="layui-icon layui-icon-search"></i>搜索</button>
                    <a href="javascript:void();" class="layui-btn layui-btn-warm" onclick="start_settle()"><i
                            class="layui-icon layui-icon-add-circle"></i> 开始预结算</a>
                    <a href="javascript:void();" class="layui-btn layui-btn-warm" onclick="download_settle_table()"><i
                            class="layui-icon layui-icon-add-circle"></i> 下载结算表格</a>
                    <a href="javascript:void();" type="button" class="layui-btn" id="test1">
                        <i class="layui-icon">&#xe67c;</i>上传结算确认单
                    </a>
                </div>
            </form>
            <div class="layui-form" id="table-list">
@@ -42,7 +54,7 @@
        <script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/http_api.js"></script>
        <!-- <script src="js/common.js" type="text/javascript" charset="utf-8"></script> -->
        <script>
        <script>
            function delete_agent(id) {
                layer.confirm('您确定要执行此操作吗?', {
                    icon: 3,
@@ -65,38 +77,66 @@
                    });
                }.bind(this));
            }
            function add_agent(){
                if($("input[name=day]").val().length<1){
                        layer.msg("请选择日期");
                        return false;
            function start_settle() {
                if ($("input[name=day]").val().length < 1) {
                    layer.msg("请选择日期");
                    return false;
                }
                $.post("/admin/api/agentsettle/startSettle", {
                    day: $("input[name=day]").val()
                }, function(response) {
                    if (response.code == 0) {
                        layer.msg("预结算成功");
                    } else {
                        layer.msg(response.msg);
                    }
                    $.post("/admin/api/agentsettle/startSettle",$("input[name=day]").val(), function(response) {
                        if (response.code == 0) {
                            layer.msg("预结算成功");
                        } else {
                            layer.msg(response.msg);
                        }
                    }, 'json').fail(function(jqXHR, textStatus, errorThrown) {
                        layer.msg("网络请求失败");
                    });
                }, 'json').fail(function(jqXHR, textStatus, errorThrown) {
                    layer.msg("网络请求失败");
                });
            }
            layui.use(['form', 'jquery', 'layer', 'table', 'laydate'], function() {
            function download_settle_table() {
                if ($("input[name=day]").val().length < 1) {
                    layer.msg("请选择日期");
                    return false;
                }
                var href = "/admin/api/agentsettle/downloadSettleTable?day=" + $("input[name=day]").val();
                window.open(href, '_blank');
            }
            layui.use(['form', 'jquery', 'layer', 'table', 'laydate', 'upload'], function() {
                var table = layui.table;
                var form = layui.form;
                var $ = layui.jquery;
                var upload = layui.upload;
                var laydate = layui.laydate;
            laydate.render({
                    elem: "input[name=day]",
                    type: "date",
                    value: new Date(),
                    isInitValue:false
                });
                laydate.render({
                    elem: "input[name=day]",
                    type: "date",
                    value: new Date(),
                    isInitValue: false
                });
                //执行实例
                var uploadInst = upload.render({
                    elem: '#test1' //绑定元素
                        ,
                    accept: 'file',
                    url: '/admin/api/agentsettle/uploadSettleExcel' //上传接口
                        ,
                    done: function(res) {
                        //上传完毕回调
                        layer.msg("结算完成")
                    },
                    error: function() {
                        //请求异常回调
                        layer.msg("结算异常")
                    }
                });
                let table_option = {
                    elem: '#table',
                    url: '/admin/api/agentsettle/list', //数据接口
@@ -139,15 +179,15 @@
                                title: 'ID',
                                width: 100,
                                fixed: 'left'
                            },{
                            }, {
                                field: 'agentId',
                                title: '渠道',
                                width: 100,
                                sort: false,
                                templet: function(d) {
                                  return d["agent.name"]+"("+d["agent.id"]+")";
                                    return d["agent.name"] + "<br>" + d["agent.id"];
                                }
                            },  {
                            }, {
                                field: 'settleDay',
                                title: '日期',
                                width: 120
@@ -156,6 +196,9 @@
                                title: '预结算金额',
                                width: 100,
                                sort: false,
                                templet: function(d) {
                                    return "¥" + d.settleMoney;
                                }
                            }, {
                                field: 'createTime',
                                title: '预结算时间',
@@ -163,25 +206,40 @@
                            }, {
                                field: 'actualSettleMoney',
                                title: '实际结算金额',
                                width: 100,
                                sort: false
                                width: 150,
                                sort: false,
                                templet: function(d) {
                                    if (d.actualSettleMoney != undefined && d.actualSettleMoney.length > 0) {
                                        return "¥" + d.actualSettleMoney;
                                    } else {
                                        return "";
                                    }
                                }
                            }, {
                                field: 'settleTime',
                                title: '实际结算时间',
                                width: 100,
                                width: 180,
                                sort: false
                            }, {
                                field: 'statusDesc',
                                field: '',
                                title: '状态',
                                width: 100,
                                sort: false
                            },  {
                                width: 150,
                                templet: function(d) {
                                    if (d.status == 0) {
                                        return "结算待确认";
                                    } else {
                                        return "结算已确认";
                                    }
                                }
                            }, {
                                field: '',
                                title: '操作',
                                width: 100,
                                sort: false,
                                templet: function(d) {
                                    var html = "";
                                    html += "<div><a href='javascript:void' onclick='delete_agent(" + d.id + ")' class='layui-table-link'>删除</a></div>";
                                    html += "<div><a href='javascript:void' onclick='delete_agent(" + d.id +
                                        ")' class='layui-table-link'>删除</a></div>";
                                    return html;
                                }
                            }
@@ -210,9 +268,9 @@
                    });
                    return false;
                });
            });
        </script>
    </body>
</html>
</html>
src/main/resources/static/admin/agent-withdraw-list.html
@@ -5,8 +5,9 @@
        <meta charset="UTF-8">
        <meta name="renderer" content="webkit">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title>设备列表</title>
        <meta name="viewport"
            content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <title>提现列表</title>
        <link rel="stylesheet" type="text/css" href="layui/css/layui.css" />
        <link rel="stylesheet" type="text/css" href="css/admin.css" />
        <style>
@@ -20,19 +21,32 @@
        <div class="page-content-wrap">
            <form class="layui-form" action="" lay-filter='search'>
                <div class="layui-form-item">
                    <div class="layui-inline">
                        <input type="text" name="key" id="key" placeholder="按渠道ID/名称搜索" autocomplete="off" class="layui-input">
                        <input type="text" name="key" id="key" placeholder="按渠道ID搜索" autocomplete="off"
                            class="layui-input">
                    </div>
                    <div class="layui-inline">
                        <input type="text" name="day" placeholder="日期" autocomplete="off" class="layui-input">
                        <input type="text" name="startTime" placeholder="开始时间" autocomplete="off" class="layui-input">
                    </div>
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i class="layui-icon layui-icon-search"></i>搜索</button>
                    <a href="javascript:void();" class="layui-btn layui-btn-warm" onclick="add_agent()"><i class="layui-icon layui-icon-add-circle"></i> 开始预结算</a>
                    <div class="layui-inline">
                        <input type="text" name="endTime" placeholder="结束时间" autocomplete="off" class="layui-input">
                    </div>
                    <div class="layui-inline">
                        <select name="status">
                            <option value="-1">全部</option>
                            <option value="2">未处理</option>
                            <option value="3">已通过</option>
                            <option value="4">已驳回</option>
                        </select>
                    </div>
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i
                            class="layui-icon layui-icon-search"></i>搜索</button>
                    <a href="javascript:void();" class="layui-btn" id="pass"> 通过</a>
                    <a href="javascript:void();" class="layui-btn layui-btn-danger" id="reject"> 驳回</a>
                </div>
            </form>
            <div class="layui-form" id="table-list">
                <table class="layui-table" lay-even lay-skin="nob" id="table">
                <table class="layui-table" lay-even lay-skin="nob" id="table" lay-filter="table">
                </table>
            </div>
@@ -42,19 +56,30 @@
        <script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/http_api.js"></script>
        <!-- <script src="js/common.js" type="text/javascript" charset="utf-8"></script> -->
        <script>
            function delete_agent(id) {
        <script>
            function passWidthdraw() {
                var checkStatus = table.checkStatus('table');
                if(checkStatus.data.length<=0){
                        layer.msg("尚未选中数据");
                    return;
                }
                console.log(checkStatus.data) //获取选中行的数据
                let ids=[];
                checkStatus.data.forEach(function(e){
                    ids.push(e.id);
                });
                layer.confirm('您确定要执行此操作吗?', {
                    icon: 3,
                    title: '提示'
                }, function(index) {
                    // 确认操作
                    layer.close(index);
                    $.post("/admin/api/agentsettle/delete", {
                        "id": id
                    $.post("/admin/api/agentsettle/pass", {
                        "ids": JSON.stringify(ids)
                    }, function(response) {
                        if (response.code == 0) {
                            layer.msg("删除成功");
                            layer.msg("执行成功");
                            $("#search").click();
                        } else {
                            layer.msg(response.msg);
@@ -65,41 +90,65 @@
                    });
                }.bind(this));
            }
            function add_agent(){
            function rejectWidthdraw() {
                var checkStatus = table.checkStatus('table');
                if(checkStatus.data.length<=0){
                        layer.msg("尚未选中数据");
                    return;
                }
                console.log(checkStatus.data) //获取选中行的数据
                let ids=[];
                checkStatus.data.forEach(function(e){
                    ids.push(e.id);
                });
                
                if($("input[name=day]").val().length<1){
                        layer.msg("请选择日期");
                        return false;
                    }
                    $.post("/admin/api/agentsettle/startSettle",$("input[name=day]").val(), function(response) {
                layer.confirm('您确定要执行此操作吗?', {
                    icon: 3,
                    title: '提示'
                }, function(index) {
                    // 确认操作
                    layer.close(index);
                    $.post("/admin/api/agentsettle/reject", {
                        "ids": JSON.stringify(ids)
                    }, function(response) {
                        if (response.code == 0) {
                            layer.msg("预结算成功");
                            layer.msg("执行成功");
                            $("#search").click();
                        } else {
                            layer.msg(response.msg);
                        }
                    }, 'json').fail(function(jqXHR, textStatus, errorThrown) {
                        layer.close(index);
                        layer.msg("网络请求失败");
                    });
                }.bind(this));
            }
            layui.use(['form', 'jquery', 'layer', 'table', 'laydate'], function() {
                var table = layui.table;
                var form = layui.form;
                var $ = layui.jquery;
                var laydate = layui.laydate;
            laydate.render({
                    elem: "input[name=day]",
                    type: "date",
                    value: new Date(),
                    isInitValue:false
                });
                laydate.render({
                    elem: "input[name=startTime]",
                    type: "datetime",
                    value: new Date(),
                    isInitValue: false
                });
                laydate.render({
                    elem: "input[name=endTime]",
                    type: "datetime",
                    value: new Date(),
                    isInitValue: false
                });
                let table_option = {
                    id: "table",
                    elem: '#table',
                    url: '/admin/api/agentsettle/list', //数据接口
                    url: '/admin/api/agentsettle/listWithdraw', //数据接口
                    where: {
                        'key': $("#key").val(),
                        'day': $("input[name=day]").val(),
@@ -135,71 +184,102 @@
                    cols: [
                        [ //表头
                            {
                                checkbox: true
                            },
                            {
                                field: 'id',
                                title: 'ID',
                                width: 100,
                                fixed: 'left'
                            },{
                            }, {
                                field: 'agentId',
                                title: '渠道',
                                width: 100,
                                sort: false,
                                templet: function(d) {
                                  return d["agent.name"]+"("+d["agent.id"]+")";
                                    return d["agent.name"] + "<br>" + d["agent.id"];
                                }
                            },  {
                            },
                            {
                                field: 'settleDay',
                                title: '日期',
                                width: 120
                            }, {
                                field: 'settleMoney',
                                title: '预结算金额',
                                title: '订单发生日期',
                                width: 100,
                                sort: false,
                            },
                            {
                                field: 'actualSettleMoney',
                                title: '提现金额',
                                width: 100,
                                sort: false,
                            }, {
                                field: 'createTime',
                                title: '预结算时间',
                                field: 'alipayName',
                                title: '支付宝姓名',
                                width: 100,
                                sort: false,
                            }, {
                                field: 'alipayAccount',
                                title: '支付宝账号',
                                width: 100,
                                sort: false,
                            },
                            {
                                field: 'withDrawApplyTime',
                                title: '发起提现时间',
                                width: 180,
                            }, {
                                field: 'actualSettleMoney',
                                title: '实际结算金额',
                                width: 100,
                                sort: false
                            }, {
                                field: 'settleTime',
                                title: '实际结算时间',
                                width: 100,
                                sort: false
                            }, {
                                field: 'statusDesc',
                            },
                            {
                                field: '',
                                title: '状态',
                                width: 100,
                                sort: false,
                                templet: function(d) {
                                    switch (d.status) {
                                        case 2:
                                            return "未处理";
                                        case 3:
                                            return "支付成功";
                                        case 4:
                                            return "被驳回";
                                        default:
                                            return "未知";
                                    }
                                }
                            },
                            {
                                field: 'withDrawProcessTime',
                                title: '处理时间',
                                width: 100,
                                sort: false
                            },  {
                            },
                            {
                                field: '',
                                title: '操作',
                                sort: false,
                                templet: function(d) {
                                    var html = "";
                                    html += "<div><a href='javascript:void' onclick='delete_agent(" + d.id + ")' class='layui-table-link'>删除</a></div>";
                                    html += "<div><a href='javascript:void' onclick='delete_agent(" + d.id +
                                        ")' class='layui-table-link'>删除</a></div>";
                                    return html;
                                }
                            }
                        ]
                    ]
                };
                var key = http_util.getQueryString("key");
                if (key != null && key != undefined) {
                    form.val("search", {
                        "key": key
                    });
                    table_option.data = [];
                    setTimeout(function() {
                        $("#search").click();
                    }, 100);
                }
                //第一个实例
                let tableIns = table.render(table_option);
                table.on('checkbox(table)', function(obj) {
                    console.log(obj)
                    // 查询是否要选中
                    var checkStatus = table.checkStatus('table');
                    if (checkStatus.data.length <= 0) {
                        $("#pass").addClass("disabled");
                        $("#reject").addClass("disabled");
                    } else {
                        $("#pass").removeClass("disabled");
                        $("#reject").removeClass("disabled");
                    }
                });
                //监听提交
                form.on('submit(search)', function(data) {
                    tableIns.reload({
@@ -210,9 +290,9 @@
                    });
                    return false;
                });
            });
        </script>
    </body>
</html>
</html>
src/main/resources/static/admin/index.html
@@ -36,7 +36,7 @@
                    <dl class="layui-nav-child">
                                        <dd><a href="javascript:;" data-url="agent-list.html" data-id='10' data-text="渠道代理"><span class="l-line"></span>渠道代理</a></dd>
                                      <dd><a href="javascript:;" data-url="agent-settle-list.html" data-id='11' data-text="代理结算"><span class="l-line"></span>代理结算</a></dd>
                                      <dd><a href="javascript:;" data-url="agent-withdraw-list.html" data-id='11' data-text="代理提现"><span class="l-line"></span>代理提现</a></dd>
                                      <dd><a href="javascript:;" data-url="agent-withdraw-list.html" data-id='12' data-text="代理提现"><span class="l-line"></span>代理提现</a></dd>
                    </dl>
                  </li>
                  <li class="layui-nav-item">
src/main/resources/static/agent/css/index.css
@@ -4,7 +4,38 @@
}
[v-cloak] {
  display: none;
    display: none;
}
.top {
    width: 100%;
    height: 0.92rem;
    background: linear-gradient(0deg, rgba(206, 227, 255, 0.42), rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0.42), rgba(196, 221, 255, 0.42));
    font-size: 0.36rem;
    color: #B149E8;
    text-align: center;
    line-height: 0.92rem;
    display: flex;
    align-items: center;
    justify-content: center;
}
.top>img:first-child {
    width: 0.44rem;
    height: 0.44rem;
    margin-right: 0.18rem;
}
.top>img:last-child {
    margin-left: 0.19rem;
    width: 0.18rem;
    height: 0.31rem;
}
.nav-container {
@@ -16,7 +47,7 @@
    justify-content: center;
    position: fixed;
    z-index: 1000;
    top: 0;
    top: 0.92rem;
}
@@ -59,7 +90,7 @@
    flex-direction: column;
    flex: 1;
    position: fixed;
    top: 0.9rem;
    top: 1.82rem;
    height: 100%;
    width: 100%;
}
@@ -298,15 +329,18 @@
.content-withdraw {
    position: fixed;
    top: 0.9rem;
    top: 1.82rem;
    bottom: 1.02rem;
    height: 100%;
    overflow: auto;
    width: 100%;
    display: flex;
    flex-direction: column;
}
.content-withdraw>.list {
    margin-bottom: 1.02rem;
    flex: 1;
    height: 100%;
}
.content-withdraw>.list>div {
@@ -338,6 +372,7 @@
    color: #787878;
    display: block;
    margin-bottom: 0.18rem;
    margin-top: 0.05rem;
}
.content-withdraw>.list>div>.item>.time {
@@ -414,22 +449,23 @@
    height: 0.29rem;
}
.loading{
.loading {
    text-align: center;
    font-size: 0.3rem;
    line-height: 0.8rem;
    height: 0.8rem;
    color: #333333;
    width: 100%;
    margin-bottom: 1.92rem;
}
.loading-no{
.loading-no {
    color: #888888;
}
.empty{
.empty {
    color: #888888;
    height: 100%;
    text-align: center;
src/main/resources/static/agent/img/icon_input_miandan.png
src/main/resources/static/agent/img/icon_miandan.png
src/main/resources/static/agent/index.html
@@ -26,6 +26,18 @@
    <body>
        <div id="container" v-cloak>
            <div class="top" @click="to_miandan">
                <img src="img/icon_miandan.png"/>
                <span>查看免单福利</span>
                <img src="img/icon_input_miandan.png"/>
            </div>
            <div class="nav-container">
                <div @click="change_mode(0)">
                    <div class="nav" :class="{'active':mode==0}" > 对账</div>
@@ -115,24 +127,24 @@
                <div class="list">
                    <div v-for="(item,index) in withdraw_records">
                        <div class="month">2024年07月</div>
                        <div class="month" v-if="index==0||withdraw_records[index-1].month!=withdraw_records[index].month">{{item.month}}</div>
                        <div class="item">
                            <div class="time">2020.06.18-17:00:00</div>
                            <div class="time">{{item.settleTime}}</div>
                            <div>
                                <div>
                                    <div class="title">订单数量</div>
                                    <div class="val">12</div>
                                    <div class="val">{{item.orderCount}}</div>
                                </div>
                                <div>
                                    <div class="title">提现金额</div>
                                    <div class="val">¥100.00</div>
                                    <div class="val">¥{{item.money}}</div>
                                </div>
                                <div>
                                    <div class="title">提现状态</div>
                                    <div class="val">未提现</div>
                                    <div class="val">{{item.statusDesc}}</div>
                                </div>
                                <div class="btn">提现</div>
                                <div class="btn" :class="{'active':item.status == 1}" @click="withdraw(item.id)">提现</div>
                            </div>
src/main/resources/static/agent/js/index.js
@@ -8,23 +8,30 @@
            screen_time_index: 0,
            orders: [],
            hasMore: true,
            withdraw_records: [1, 1, 1, 1, 1],
            withdraw_records: [],
            order_statistic: {
                user_count: 0,
                order_count: 0,
                total_money: 0.00
            }
            },
            config:{}
        },
        mounted: function() {
            this.$nextTick(function() {
                app.getConfig();
                app.search();
            });
        },
        methods: {
            to_miandan:function(){
                window.location.href=app.config.submitKeyUrl;
            },
            change_mode: function(mode) {
                app.mode = mode;
                if(mode==1){
                    app.withdrawList();
                }
            },
            open_screen: function() {
                app.temp_screen_time_index = app.screen_time_index;
@@ -61,20 +68,28 @@
            setAlipayAccount: function() {
                window.location.href = "alipay_account_setting.html";
            },
            search: function() {
                app.current_page = 1;
                app.orders = [];
                app.order_statistic = {
                    user_count: 0,
                    order_count: 0,
                    total_money: 0.00
                };
            getConfig:function(){
                http_util.post("/agentapi/admin/getConfig", {
                }, function(res) {
                    if (res.code == 0) {
                        app.config = res.data;
                    }
                }, function(res) {
                });
            },
            requestOrders: function() {
                http_util.post("/agentapi/admin/orderList", {
                    key: $("#search_key").val(),
                    timeIndex: app.screen_time_index,
                    page: app.current_page
                }, function(res) {
                    if (res.code == 0) {
                        if (app.current_page == 1) {
                            app.orders = [];
                        }
                        app.orders = app.orders.concat(res.data.list);
                        app.order_statistic.order_count = res.data.count;
                        app.order_statistic.user_count = res.data.statistic.count;
@@ -90,15 +105,70 @@
                });
            },
            requestWithdraw: function() {
                http_util.post("/agentapi/admin/withdrawList", {
                    page: app.current_page
                }, function(res) {
                    if (res.code == 0) {
                        if (app.current_page == 1) {
                            app.withdraw_records = [];
                        }
                        app.withdraw_records = app.withdraw_records.concat(res.data.list);
                        if (app.withdraw_records.length <res.data.count) {
                            app.hasMore = true;
                        } else {
                            app.hasMore = false;
                        }
                    }
                }, function(res) {
                });
            },
            search: function() {
                app.current_page = 1;
                app.order_statistic = {
                    user_count: 0,
                    order_count: 0,
                    total_money: 0.00
                };
                app.requestOrders();
            },
            withdrawList:function(){
                app.current_page = 1;
                app.requestWithdraw();
            },
            loadOrder: function() {
                if(!app.hasMore){
                    return;
                }
                app.current_page += 1;
                app.requestOrders();
            },
            loadWidthdraw: function() {
                if(!app.hasMore){
                    return;
                }
                app.current_page += 1;
                app.requestWithdraw();
            },
            withdraw:function(id){
                http_util.post("/agentapi/admin/withdraw", {
                    id: id
                }, function(res) {
                    if (res.code == 0) {
                        app.withdrawList();
                    }else{
                        layer.msg(res.msg);
                    }
                }, function(res) {
                });
            }
        }
    });
});
});