Administrator
2025-04-21 a217652d33c75df23202828000d82d0ca8555ac2
初步完成积分后台管理相关页面
12个文件已修改
16个文件已添加
1724 ■■■■■ 已修改文件
pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/AdminAgentController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/credit/CreditExchangeRateAdminController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/credit/CreditSettingAdminController.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/credit/UserCreditExchangeRecordAdminController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/controller/admin/credit/UserCreditRecordAdminController.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/dao/agent/ChannelAgentSettingsMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/entity/agent/ChannelAgentSettings.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/factory/AgentFactory.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/credit/ExchangeRateService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/impl/KeyOrderServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/service/impl/credit/ExchangeRateServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/utils/AlipayUtil.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/vo/admin/AdminChannelAgentVO.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/vo/admin/UserCreditExchangeRecordSearchVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/taoke/autopay/vo/admin/UserCreditRecordSearchVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/alipay/alipayCertPublicKey_RSA2.crt 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/alipay/alipayRootCert.crt 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/alipay/appCertPublicKey_2021004141681244.crt 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/ChannelAgentSettingsMapper.xml 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/agent-update.html 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit-setting-edit.html 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit-setting-list.html 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit/credit-exchange-record-list.html 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit/credit-record-list.html 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit/exchange-rate-list.html 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/admin/credit/exchange-rate-update.html 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/static/credit/index.html 210 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -212,6 +212,19 @@
        </dependency>
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-easysdk</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.9.71.ALL</version>
        </dependency>
        <dependency>
            <groupId>com.yeshi</groupId>
            <artifactId>utils</artifactId>
            <version>1.0.0</version>
src/main/java/com/taoke/autopay/controller/admin/AdminAgentController.java
@@ -162,6 +162,7 @@
                .endSubmitTime(StringUtil.isNullOrEmpty(vo.getEndSubmitTime()) ? null : vo.getEndSubmitTime())
                .maxKeyCountPerDay(StringUtil.isNullOrEmpty(vo.getMaxKeyCountPerDay()) ? null : Long.parseLong(vo.getMaxKeyCountPerDay()))
                .maxPayMoneyPerDay(StringUtil.isNullOrEmpty(vo.getMaxPayMoneyPerDay()) ? null : new BigDecimal(vo.getMaxPayMoneyPerDay()))
                .creditEnable(vo.getCreditEnable() != null && vo.getCreditEnable() > 0)
                .build();
        channelAgentSettingService.add(settings);
        // 添加分成比例设置
src/main/java/com/taoke/autopay/controller/admin/credit/CreditExchangeRateAdminController.java
New file
@@ -0,0 +1,107 @@
package com.taoke.autopay.controller.admin.credit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.dao.credit.ExchangeRateMapper;
import com.taoke.autopay.entity.credit.ExchangeRate;
import com.taoke.autopay.service.credit.ExchangeRateService;
import com.taoke.autopay.utils.JsonUtil;
import com.taoke.autopay.utils.TimeUtil;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("/admin/api/credit/exchangeRate")
public class CreditExchangeRateAdminController {
    private Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
        @Override
        public void write(JsonWriter out, Date value) throws IOException {
            String desc = "";
            if (value != null) {
                // 判断是否是同一天
                desc = TimeUtil.getGernalTime(value.getTime(), "yyyy-MM-dd HH:mm:ss");
                out.value(desc);
            } else {
                out.value("");
            }
        }
        @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();
    @Resource
    private ExchangeRateService exchangeRateService;
    /**
     * 获取积分汇率列表
     */
    @ResponseBody
    @RequestMapping("list")
    public String listExchangeRates(int page, int limit) {
        List<ExchangeRate> exchangeRates = exchangeRateService.listExchangeRates(ExchangeRateMapper.DaoQuery.builder()
                        .start((long) (page - 1) * limit)
                        .count(limit)
                .build());
        long count = exchangeRateService.countExchangeRates(ExchangeRateMapper.DaoQuery.builder().build());
        JSONObject root=new JSONObject();
        root.put("list",gson.toJson(exchangeRates));
        root.put("count",count);
        return JsonUtil.loadTrueResult(root);
    }
    /**
     * 获取积分汇率详情
     */
    @ResponseBody
    @RequestMapping("getDetail")
    public String getDetail(Long id) {
        ExchangeRate exchangeRate = exchangeRateService.selectById(id);
        if (exchangeRate == null) {
            return JsonUtil.loadFalseResult("积分汇率不存在");
        }
        return JsonUtil.loadTrueResult(gson.toJson(exchangeRate));
    }
    /**
     * 更新积分汇率
     */
    @ResponseBody
    @RequestMapping("update")
    public String updateExchangeRate(ExchangeRate exchangeRate) {
        int success = exchangeRateService.updateExchangeRate(exchangeRate);
        if (success>0) {
            return JsonUtil.loadTrueResult("更新成功");
        } else {
            return JsonUtil.loadFalseResult("更新失败");
        }
    }
}
src/main/java/com/taoke/autopay/controller/admin/credit/CreditSettingAdminController.java
New file
@@ -0,0 +1,87 @@
package com.taoke.autopay.controller.admin.credit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.entity.credit.CreditSetting;
import com.taoke.autopay.service.credit.CreditSettingService;
import com.taoke.autopay.utils.TimeUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.yeshi.utils.JsonUtil;
import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("/admin/api/credit/setting")
public class CreditSettingAdminController {
    private Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
        @Override
        public void write(JsonWriter out, Date value) throws IOException {
            String desc = "";
            if (value != null) {
                // 判断是否是同一天
                desc = TimeUtil.getGernalTime(value.getTime(), "yyyy-MM-dd HH:mm:ss");
                out.value(desc);
            } else {
                out.value("");
            }
        }
        @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();
    @Resource
    private CreditSettingService creditSettingService;
    /**
     * 获取积分设置列表
     */
    @ResponseBody
    @RequestMapping("list")
    public String list(int page, int limit) {
        List<CreditSetting> settingList = creditSettingService.listAllSettings();
        long count = settingList.size();
        return JsonUtil.loadTrueResult(gson.toJson(settingList));
    }
    /**
     * 修改积分设置
     */
    @ResponseBody
    @RequestMapping("update")
    public String update(CreditSetting setting) {
        int result = creditSettingService.updateSetting(setting);
        if (result > 0) {
            return JsonUtil.loadTrueResult("修改成功");
        } else {
            return JsonUtil.loadFalseResult("修改失败");
        }
    }
}
src/main/java/com/taoke/autopay/controller/admin/credit/UserCreditExchangeRecordAdminController.java
New file
@@ -0,0 +1,127 @@
package com.taoke.autopay.controller.admin.credit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.dao.credit.CreditExchangeRecordMapper;
import com.taoke.autopay.entity.credit.CreditExchangeRecord;
import com.taoke.autopay.exception.UserCreditExchangeException;
import com.taoke.autopay.manager.UserCreditExchangeManager;
import com.taoke.autopay.service.credit.CreditExchangeRecordService;
import com.taoke.autopay.utils.JsonUtil;
import com.taoke.autopay.utils.StringUtil;
import com.taoke.autopay.utils.TimeUtil;
import com.taoke.autopay.vo.admin.UserCreditExchangeRecordSearchVO;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("/admin/api/credit/exchange-record")
public class UserCreditExchangeRecordAdminController {
    @Resource
    private UserCreditExchangeManager userCreditExchangeManager;
    private Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
        @Override
        public void write(JsonWriter out, Date value) throws IOException {
            String desc = "";
            if (value != null) {
                desc = TimeUtil.getGernalTime(value.getTime(), "yyyy-MM-dd HH:mm:ss");
                out.value(desc);
            } else {
                out.value("");
            }
        }
        @Override
        public Date read(JsonReader in) throws IOException {
            return new Date();
        }
    }).create();
    @Resource
    private CreditExchangeRecordService userCreditExchangeRecordService;
    /**
     * 获取积分兑换记录列表
     */
    @ResponseBody
    @RequestMapping("list")
    public String listRecords(UserCreditExchangeRecordSearchVO search, int page, int limit) {
        CreditExchangeRecordMapper.DaoQuery query =  CreditExchangeRecordMapper.DaoQuery.builder().build();
        if (!StringUtil.isNullOrEmpty(search.getUid())) {
            query.uid = Long.parseLong(search.getUid());
        }
        if (search.getStatus() != null) {
            query.exchangeStatus = search.getStatus();
        }
        if (search.getStartDate() != null) {
            query.minCreateTime = new Date(TimeUtil.convertToTimeTemp(search.getStartDate(), "yyyy-MM-dd"));
        }
        if (search.getEndDate() != null) {
            query.maxCreateTime = new Date(TimeUtil.convertToTimeTemp(search.getEndDate(), "yyyy-MM-dd"));
        }
        query.sortList = Arrays.asList(new String[]{"_create_time desc"});
        query.start = (long) (page - 1) * limit;
        query.count = limit;
        List<CreditExchangeRecord> records = userCreditExchangeRecordService.listExchangeRecords(query);
        long count = userCreditExchangeRecordService.countExchangeRecords(query);
        JSONObject root = new JSONObject();
        root.put("list", gson.toJson(records));
        root.put("count", count);
        return JsonUtil.loadTrueResult(root);
    }
    /**
     * 批量通过兑换记录
     */
    @ResponseBody
    @RequestMapping("approve")
    public String approveRecords(Long[] ids) {
        int successCount = 0;
        int failCount = 0;
        for(Long id :ids) {
            try {
                userCreditExchangeManager.approveExchange(id);
                successCount+=1;
            } catch (UserCreditExchangeException e) {
                failCount+=1;
            }
        }
        return JsonUtil.loadTrueResult(String.format("成功通过 %d 条记录 通过异常 %d 条记录", successCount, failCount));
    }
    /**
     * 批量驳回兑换记录
     */
    @ResponseBody
    @RequestMapping("reject")
    public String rejectRecords(Long[] ids) {
        int successCount = 0;
        int failCount = 0;
        for(Long id :ids) {
            try {
                userCreditExchangeManager.rejectExchange(id);
                successCount+=1;
            } catch (UserCreditExchangeException e) {
                failCount+=1;
            }
        }
        return JsonUtil.loadTrueResult(String.format("成功驳回 %d 条记录 驳回异常 %d 条记录", successCount, failCount));
    }
}
src/main/java/com/taoke/autopay/controller/admin/credit/UserCreditRecordAdminController.java
New file
@@ -0,0 +1,100 @@
package com.taoke.autopay.controller.admin.credit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.taoke.autopay.dao.credit.UserCreditRecordMapper;
import com.taoke.autopay.entity.credit.UserCreditRecord;
import com.taoke.autopay.service.credit.UserCreditRecordService;
import com.taoke.autopay.utils.JsonUtil;
import com.taoke.autopay.utils.StringUtil;
import com.taoke.autopay.utils.TimeUtil;
import com.taoke.autopay.vo.admin.UserCreditRecordSearchVO;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Controller
@RequestMapping("/admin/api/credit/record")
public class UserCreditRecordAdminController {
    private Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new TypeAdapter<Date>() {
        @Override
        public void write(JsonWriter out, Date value) throws IOException {
            String desc = "";
            if (value != null) {
                // 判断是否是同一天
                desc = TimeUtil.getGernalTime(value.getTime(), "yyyy-MM-dd HH:mm:ss");
                out.value(desc);
            } else {
                out.value("");
            }
        }
        @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();
    @Resource
    private UserCreditRecordService userCreditRecordService;
    /**
     * 获取积分记录列表
     */
    @ResponseBody
    @RequestMapping("list")
    public String listRecords(UserCreditRecordSearchVO search, int page, int limit) {
        UserCreditRecordMapper.DaoQuery query = new UserCreditRecordMapper.DaoQuery();
        if(!StringUtil.isNullOrEmpty(search.getUid())){
            query.uid = Long.parseLong(search.getUid());
        }
        if(search.getDirection()!=null){
            query.direction = search.getDirection();
        }
        if(search.getStartDate()!=null){
            query.minCreateTime = new Date(TimeUtil.convertToTimeTemp(search.getStartDate(),"yyyy-MM-dd"));
        }
        if(search.getEndDate()!=null){
            query.maxCreateTime = new Date(TimeUtil.convertToTimeTemp(search.getEndDate(),"yyyy-MM-dd"));
        }
        query.sortList = Arrays.asList(new String[]{ "_create_time desc"});
        query.start = (long) (page - 1) * limit;
        query.count = limit;
        List<UserCreditRecord> records = userCreditRecordService.listCreditRecords(query);
        long count = userCreditRecordService.countCreditRecords(query);
        JSONObject root = new JSONObject();
        root.put("list", gson.toJson(records));
        root.put("count", count);
        return JsonUtil.loadTrueResult(root);
    }
}
src/main/java/com/taoke/autopay/dao/agent/ChannelAgentSettingsMapper.java
@@ -32,5 +32,6 @@
        public long start;
        public int count;
        public List<String> sortList;
        public Boolean creditEnable;
    }
}
src/main/java/com/taoke/autopay/entity/agent/ChannelAgentSettings.java
@@ -50,6 +50,10 @@
   */
  @Column(name="_end_submit_time")
  private String endSubmitTime;
  @Column(name ="_credit_enable")
  private Boolean creditEnable;
  @Column(name="_create_time")
  private Date createTime;
  @Column(name="_update_time")
src/main/java/com/taoke/autopay/factory/AgentFactory.java
@@ -35,6 +35,7 @@
            }
            vo.setStartSubmitTime(agentSettings.getStartSubmitTime());
            vo.setEndSubmitTime(agentSettings.getEndSubmitTime());
            vo.setCreditEnable(agentSettings.getCreditEnable()!=null&&agentSettings.getCreditEnable()?1:0);
        }
        if(baseSubmitKeyUrl!=null) {
            vo.setSubmitKeyLink(AgentUtil.getSubmitKeyUrl(baseSubmitKeyUrl, agent.getAlias()));
src/main/java/com/taoke/autopay/service/credit/ExchangeRateService.java
@@ -40,4 +40,12 @@
     * @return 汇率记录总数
     */
    long countExchangeRates(DaoQuery query);
    /**
     * 查询详情
     * @param id
     * @return
     */
    ExchangeRate selectById(Long id);
}
src/main/java/com/taoke/autopay/service/impl/KeyOrderServiceImpl.java
@@ -10,8 +10,10 @@
import com.taoke.autopay.entity.agent.ChannelAgentSettings;
import com.taoke.autopay.exception.KeyOrderException;
import com.taoke.autopay.exception.KeyVerifyException;
import com.taoke.autopay.exception.UserCreditException;
import com.taoke.autopay.exception.WxOrderCountException;
import com.taoke.autopay.factory.OrderFactory;
import com.taoke.autopay.manager.UserCreditManager;
import com.taoke.autopay.service.*;
import com.taoke.autopay.service.agent.ChannelAgentService;
import com.taoke.autopay.service.agent.ChannelAgentSettingService;
@@ -22,6 +24,8 @@
import com.taoke.autopay.vo.SubmitKeyInfo;
import com.taoke.autopay.vo.admin.IgnoreAgentOrderSettingVO;
import net.sf.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +44,8 @@
 */
@Service
public class KeyOrderServiceImpl implements KeyOrderService {
    private Logger loggerDebug= LoggerFactory.getLogger("debugLogger");
    @Resource
    private KeyOrderMapper keyOrderMapper;
@@ -70,6 +76,9 @@
    @Resource
    private WxUserService wxUserService;
    @Resource
    private UserCreditManager userCreditManager;
    @Override
@@ -208,6 +217,28 @@
            orderUpdate.setPayTime(new Date());
        }
        update(orderUpdate);
        // 增加积分
        try {
            boolean creditEnable = true;
            // 判断代理是否允许增加积分
            Long agengId = old.getAgentId();
            if(agengId!=null) {
                ChannelAgentSettings agentSettings =   channelAgentSettingService.selectByAgentId(agengId);
                if(agentSettings!=null&&agentSettings.getCreditEnable()!=null) {
                    creditEnable =agentSettings.getCreditEnable();
                }
            }
            if(creditEnable) {
                if (old.getOrderType() == Constant.ORDER_TYPE_DY) {
                    userCreditManager.addDYOrderPayRecord(old.getUid(), id);
                } else if (old.getOrderType() == Constant.ORDER_TYPE_KS) {
                    userCreditManager.addKSOrderPayRecord(old.getUid(), id);
                }
            }
        } catch (UserCreditException e) {
            loggerDebug.error("增加积分出错:"+id, e);
        }
    }
    @Transactional(rollbackFor = Exception.class)
src/main/java/com/taoke/autopay/service/impl/credit/ExchangeRateServiceImpl.java
@@ -42,4 +42,9 @@
    public long countExchangeRates(DaoQuery query) {
        return exchangeRateMapper.count(query);
    }
    @Override
    public ExchangeRate selectById(Long id) {
        return exchangeRateMapper.selectByPrimaryKey(id);
    }
}
src/main/java/com/taoke/autopay/utils/AlipayUtil.java
New file
@@ -0,0 +1,141 @@
package com.taoke.autopay.utils;
import java.math.BigDecimal;
import com.alipay.api.AlipayApiException;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import net.sf.json.JSONObject;
public class AlipayUtil {
    private static DefaultAlipayClient alipayClient = null;
    static {
        String privateKey = "";
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do");
        certAlipayRequest.setAppId("2021004141681244");
        certAlipayRequest.setPrivateKey(privateKey);
        certAlipayRequest.setFormat("json");
        certAlipayRequest.setCharset("GBK");
        certAlipayRequest.setSignType("RSA2");
        certAlipayRequest.setCertPath(
                AlipayUtil.class.getClassLoader().getResource("alipay/alipayCertPublicKey_RSA2.crt").getPath());
        certAlipayRequest.setAlipayPublicCertPath(
                AlipayUtil.class.getClassLoader().getResource("alipay/appCertPublicKey_2021004141681244.crt").getPath());
        certAlipayRequest.setRootCertPath(
                AlipayUtil.class.getClassLoader().getResource("alipay/alipayRootCert.crt").getPath());
        try {
            alipayClient = new DefaultAlipayClient(certAlipayRequest);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }
    public static AlipayFundTransUniTransferResponse transfer(String outBizNo, String account, String name,
                                                              BigDecimal money, String orderTitle, String mark)
            throws AlipayApiException, NumberFormatException, AlipayTransferException {
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        JSONObject json = new JSONObject();
        json.put("out_biz_no", outBizNo);
        json.put("trans_amount", money.toString());
        json.put("product_code", "TRANS_ACCOUNT_NO_PWD");
        json.put("order_title", orderTitle);
        JSONObject payee_info = new JSONObject();
        payee_info.put("identity", account);
        payee_info.put("identity_type", "ALIPAY_LOGON_ID");
        payee_info.put("name", name);
        json.put("payee_info", payee_info);
        json.put("remark", mark);
        json.put("biz_scene", "DIRECT_TRANSFER");
        request.setBizContent(json.toString());
        AlipayFundTransUniTransferResponse response = null;
        response = alipayClient.certificateExecute(request);
        // 成功转账
        if (response != null && response.isSuccess() && "10000".equals(response.getCode())) {
            return response;
        } else// 转账失败
        {
            throw new AlipayTransferException(Integer.parseInt(response.getCode()), response.getSubCode(),
                    response.getSubMsg());
        }
    }
    public static AlipayFundTransUniTransferResponse transferNoThrowException(String outBizNo, String account, String name,
                                                                              BigDecimal money, String orderTitle, String mark) throws AlipayApiException{
        AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
        JSONObject json = new JSONObject();
        json.put("out_biz_no", outBizNo);
        json.put("trans_amount", money.toString());
        json.put("product_code", "TRANS_ACCOUNT_NO_PWD");
        json.put("order_title", orderTitle);
        JSONObject payee_info = new JSONObject();
        payee_info.put("identity", account);
        payee_info.put("identity_type", "ALIPAY_LOGON_ID");
        payee_info.put("name", name);
        json.put("payee_info", payee_info);
        json.put("remark", mark);
        json.put("biz_scene", "DIRECT_TRANSFER");
        request.setBizContent(json.toString());
        AlipayFundTransUniTransferResponse response = null;
        response = alipayClient.certificateExecute(request);
        return response;
    }
    public static void main(String[] args) throws AlipayTransferException, AlipayApiException {
        AlipayUtil.transfer("test-test","18581318252","贺小辉",new BigDecimal("0.1"),"板栗快省提现","提现");
    }
    static class AlipayTransferException extends Exception {
        public AlipayTransferException() {
        }
        /**
         * 支付宝转账异常
         */
        private static final long serialVersionUID = 1L;
        private int code;
        private String subCode;
        private String msg;
        public int getCode() {
            return code;
        }
        public String getMsg() {
            return msg;
        }
        public String getSubCode() {
            return subCode;
        }
        public AlipayTransferException(int code, String subCode, String msg) {
            this.code = code;
            this.msg = msg;
            this.subCode = subCode;
        }
        @Override
        public String getMessage() {
            return this.msg;
        }
    }
}
src/main/java/com/taoke/autopay/vo/admin/AdminChannelAgentVO.java
@@ -31,5 +31,6 @@
    private Date createTime;
    private String shareRatioInfos;
    private String shieldedAreas;
    private Integer creditEnable;
}
src/main/java/com/taoke/autopay/vo/admin/UserCreditExchangeRecordSearchVO.java
New file
@@ -0,0 +1,14 @@
package com.taoke.autopay.vo.admin;
import lombok.Data;
import java.util.Date;
@Data
public class UserCreditExchangeRecordSearchVO {
    private String uid; // 用户ID
    private Integer status; // 状态(未审核/已通过/已拒绝)
    private String startDate; // 最小日期
    private String endDate; // 最大日期
}
src/main/java/com/taoke/autopay/vo/admin/UserCreditRecordSearchVO.java
New file
@@ -0,0 +1,14 @@
package com.taoke.autopay.vo.admin;
import lombok.Data;
import java.util.Date;
@Data
public class UserCreditRecordSearchVO {
    private String uid; // 用户ID
    private Integer direction; // 类型(获得/消耗)
    private String startDate; // 最小日期
    private String endDate; // 最大日期
}
src/main/resources/alipay/alipayCertPublicKey_RSA2.crt
New file
@@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIDsjCCApqgAwIBAgIQICQEIaRNvgGtD3dZzaeOkjANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
YXNzIDIgUjEwHhcNMjQwNDIxMDcwMDA5WhcNMjkwNDIwMDcwMDA5WjCBkjELMAkGA1UEBhMCQ04x
LTArBgNVBAoMJOmHjeW6hua1meWkj+mbhuaIkOaIv+Wxi+aciemZkOWFrOWPuDEPMA0GA1UECwwG
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
uC0yMDg4NjQxMDg4NjE0MDU3MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuk6RF+MJ
7P12v6T8lwB7Ln4MQkVo8P5Mq5jsSiRFxq8ievaAap0R7KtCvJhFUABtB07FCnK4V1s4zljB1zF+
WLexJxiGjS66ZjVsp7fPVu8wMpscxpTrnp7FcbJC9ogqGrA39DLT2mK1w0DxGjeQll5YShYRucbD
Y/7+db4DhBxQ9170s0guqmdh0E98XHzVj2TZL7VTze3Spitel8Nwf6noQiX2wZrLJ2Ar6yY+2inf
CXMHV0Uk2qpm1Bl3gDm0q6awIGx8gQLOT3Yp2hJ7N9Vs3pXk0Jq7ppOIuHZgWWPBVJStn/u4tAbE
4Kbk9m10ZWyEAWFiWsXk7lNbdc+9MwIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
AQELBQADggEBAI+SfBgwryWPdFo0lVWUaO7vA74JwMLsgEBtszYYqkIAPrlU0yifVlL240NfLASw
K4hJ4GW7fsN1iPXcPh2HatnDbc/4zQXc18UywstqnPgt+eCoEvgwnc+1wA19ZXSOad7iBx7JLxFd
GCZHqwLkq5c/t8sqiMnUgMp5sHZpk59SCYb2ziwcLNAlsX2SOrUsRel54nEL5AtBbx7XU+xik4Hf
F0U5jh/PpR+nCXup/fCiSM6/tyBWRESprbft3uKZ1GEPLsWQJa9QNAc4Y0opHJnGKDVZ1xoqYo4Q
dP/SvRMy/Ch5sjbkfuZh/f5MjB0T0pxs2ufVTUIg3HNFw6AI+f0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU
BgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEw
LwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMy
MjE0MzQxNVoXDTM3MTEyNjE0MzQxNVowgYIxCzAJBgNVBAYTAkNOMRYwFAYDVQQKDA1BbnQgRmlu
YW5jaWFsMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE5MDcGA1UEAwwwQW50IEZp
bmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDbGFzcyAyIFIxMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAsLMfYaoRoPRbmDcAfXPCmKf43pWRN5yTXa/KJWO0l+mrgQvs89bA
NEvbDUxlkGwycwtwi5DgBuBgVhLliXu+R9CYgr2dXs8D8Hx/gsggDcyGPLmVrDOnL+dyeauheARZ
fA3du60fwEwwbGcVIpIxPa/4n3IS/ElxQa6DNgqxh8J9Xwh7qMGl0JK9+bALuxf7B541Gr4p0WEN
G8fhgjBV4w4ut9eQLOoa1eddOUSZcy46Z7allwowwgt7b5VFfx/P1iKJ3LzBMgkCK7GZ2kiLrL7R
iqV+h482J7hkJD+ardoc6LnrHO/hIZymDxok+VH9fVeUdQa29IZKrIDVj65THQIDAQABo2MwYTAf
BgNVHSMEGDAWgBRfdLQEwE8HWurlsdsio4dBspzhATAdBgNVHQ4EFgQUSqHkYINtUSAtDPnS8Xoy
oP9p7qEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB
AIQ8TzFy4bVIVb8+WhHKCkKNPcJe2EZuIcqvRoi727lZTJOfYy/JzLtckyZYfEI8J0lasZ29wkTt
a1IjSo+a6XdhudU4ONVBrL70U8Kzntplw/6TBNbLFpp7taRALjUgbCOk4EoBMbeCL0GiYYsTS0mw
7xdySzmGQku4GTyqutIGPQwKxSj9iSFw1FCZqr4VP4tyXzMUgc52SzagA6i7AyLedd3tbS6lnR5B
L+W9Kx9hwT8L7WANAxQzv/jGldeuSLN8bsTxlOYlsdjmIGu/C9OWblPYGpjQQIRyvs4Cc/mNhrh+
14EQgwuemIIFDLOgcD+iISoN8CqegelNcJndFw1PDN6LkVoiHz9p7jzsge8RKay/QW6C03KNDpWZ
EUCgCUdfHfo8xKeR+LL1cfn24HKJmZt8L/aeRZwZ1jwePXFRVtiXELvgJuM/tJDIFj2KD337iV64
fWcKQ/ydDVGqfDZAdcU4hQdsrPWENwPTQPfVPq2NNLMyIH9+WKx9Ed6/WzeZmIy5ZWpX1TtTolo6
OJXQFeItMAjHxW/ZSZTok5IS3FuRhExturaInnzjYpx50a6kS34c5+c8hYq7sAtZ/CNLZmBnBCFD
aMQqT8xFZJ5uolUaSeXxg7JFY1QsYp5RKvj4SjFwCGKJ2+hPPe9UyyltxOidNtxjaknOCeBHytOr
-----END CERTIFICATE-----
src/main/resources/alipay/alipayRootCert.crt
New file
@@ -0,0 +1,88 @@
-----BEGIN CERTIFICATE-----
MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
pDoiVhsLwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
kT9qhqn+lw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
Yf4Zr0fJsGuv
-----END CERTIFICATE-----
src/main/resources/alipay/appCertPublicKey_2021004141681244.crt
New file
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIEoDCCA4igAwIBAgIQICQEIcgSUDRWjWadMdPyBDANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
YXNzIDEgUjEwHhcNMjQwNDIxMDcwMDA4WhcNMjkwNDIwMDcwMDA4WjBoMQswCQYDVQQGEwJDTjEt
MCsGA1UECgwk6YeN5bqG5rWZ5aSP6ZuG5oiQ5oi/5bGL5pyJ6ZmQ5YWs5Y+4MQ8wDQYDVQQLDAZB
bGlwYXkxGTAXBgNVBAMMEDIwODg2NDEwODg2MTQwNTcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCxFx99D1yZvxJbByFFrnttK5M6QS6HR7glg3R1jLT5hf7brVAaHi6WSJfO1HlY9UyR
coCgn8CanZtGDdPu14MUlep60qzWS9TZINx42PbLgKusX9NJjQwluQBye1li0HY/g7FfrdygqYGP
4M5TLhozlGmfkgaofP7cXxB3AIan/n/JFeaQyEfSkHIxyFi+W4iQtW+mbMzp7j/4p45NRzgqDHx0
fAalUo5C73X3gc9pZAjHissq+0y2NA9onY4n2XbgyyG/0Vu4k3mA7tncxYxj11uTnO5Pu3mRScjG
EwxFVtvdQ4w4mpDRiCOrcnjTnUHHXut1sHv8Wal5TrIDqOqTAgMBAAGjggEpMIIBJTAfBgNVHSME
GDAWgBRxB+IEYRbk5fJl6zEPyeD0PJrVkTAdBgNVHQ4EFgQU47ZLKWm55pbYju6v4fUKCjiGfnUw
QAYDVR0gBDkwNzA1BgdggRwBbgEBMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9jYS5hbGlwYXkuY29t
L2Nwcy5wZGYwDgYDVR0PAQH/BAQDAgbAMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9jYS5hbGlw
YXkuY29tL2NybDg4LmNybDBgBggrBgEFBQcBAQRUMFIwKAYIKwYBBQUHMAKGHGh0dHA6Ly9jYS5h
bGlwYXkuY29tL2NhNi5jZXIwJgYIKwYBBQUHMAGGGmh0dHA6Ly9jYS5hbGlwYXkuY29tOjgzNDAv
MA0GCSqGSIb3DQEBCwUAA4IBAQAvmrmIeaeT0KsGLMasRA1DDYiXTB1qD0oadOg8dWlH8Be9Bt1q
v8WTPxd9fOkyUGsuvYVkP+unYOfuMELhbzXpWbR1/Z6mgjkp+I7uhkhEkQsGsXeeDQ9WuENoq0M+
uKwF/z0XP8rUTXXdVrsncS8Wq5pYSR+v4XVaxDjub+qWe/wRjDz2cFi65aijt94/+2oCfnZlSo+/
o81+VMkkls4lPzn0j+ybiE+uL+DmNNK5EV5+tAy7aM3meya0Qfo9zua1mWk8qF9DL9FVP8EO3o3j
cHgQJf9Uhm2Vpn01d5QVXeP5lxOzsiKZRAcHusFesBNE95wzexGmPqCXL6wYkUO+
-----END CERTIFICATE-----
src/main/resources/mapper/ChannelAgentSettingsMapper.xml
@@ -8,10 +8,11 @@
    <result column="_max_pay_money_per_day" property="maxPayMoneyPerDay" jdbcType="DECIMAL"/>
    <result column="_start_submit_time" property="startSubmitTime" jdbcType="VARCHAR"/>
    <result column="_end_submit_time" property="endSubmitTime" jdbcType="VARCHAR"/>
    <result column="_credit_enable" property="creditEnable" jdbcType="BOOLEAN"/> <!-- 新增字段 -->
    <result column="_create_time" property="createTime" jdbcType="TIMESTAMP"/>
    <result column="_update_time" property="updateTime" jdbcType="TIMESTAMP"/>
  </resultMap>
  <sql id="Base_Column_List">_id,_max_key_count_per_day,_max_pay_money_per_day,_start_submit_time,_end_submit_time,_create_time,_update_time</sql>
  <sql id="Base_Column_List">_id,_max_key_count_per_day,_max_pay_money_per_day,_start_submit_time,_end_submit_time,_credit_enable,_create_time,_update_time</sql> <!-- 新增字段 -->
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">select
    <include refid="Base_Column_List"/> from table_agent_settings where _id = #{id,jdbcType=BIGINT}
  </select>
@@ -24,6 +25,7 @@
    <if test="query.maxPayMoneyPerDay!=null">AND _max_pay_money_per_day = #{query.maxPayMoneyPerDay}</if>
    <if test="query.startSubmitTime!=null">AND _start_submit_time = #{query.startSubmitTime}</if>
    <if test="query.endSubmitTime!=null">AND _end_submit_time = #{query.endSubmitTime}</if>
    <if test="query.creditEnable!=null">AND _credit_enable = #{query.creditEnable}</if> <!-- 新增字段 -->
    <if test="query.minCreateTime!=null">AND _create_time >= #{query.minCreateTime}</if>
    <if test="query.maxCreateTime!=null">AND #{query.maxCreateTime} > _create_time</if>
    <if test="query.minUpdateTime!=null">AND _update_time >= #{query.minUpdateTime}</if>
@@ -40,37 +42,57 @@
    <include refid="listWhereSQL"/>
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from table_agent_settings where _id = #{id,jdbcType=BIGINT}</delete>
  <insert id="insert" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings" useGeneratedKeys="true" keyProperty="id">insert into table_agent_settings (_id,_max_key_count_per_day,_max_pay_money_per_day,_start_submit_time,_end_submit_time,_create_time,_update_time) values (#{id,jdbcType=BIGINT},#{maxKeyCountPerDay,jdbcType=BIGINT},#{maxPayMoneyPerDay,jdbcType=DECIMAL},#{startSubmitTime,jdbcType=VARCHAR},#{endSubmitTime,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP})</insert>
  <insert id="insertSelective" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings" useGeneratedKeys="true" keyProperty="id">insert into table_agent_settings
  <insert id="insert" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings" useGeneratedKeys="true" keyProperty="id">
    insert into table_agent_settings (_id,_max_key_count_per_day,_max_pay_money_per_day,_start_submit_time,_end_submit_time,_credit_enable,_create_time,_update_time)
    values (#{id,jdbcType=BIGINT},#{maxKeyCountPerDay,jdbcType=BIGINT},#{maxPayMoneyPerDay,jdbcType=DECIMAL},#{startSubmitTime,jdbcType=VARCHAR},#{endSubmitTime,jdbcType=VARCHAR},#{creditEnable,jdbcType=BOOLEAN},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings" useGeneratedKeys="true" keyProperty="id">
    insert into table_agent_settings
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">_id,</if>
      <if test="maxKeyCountPerDay != null">_max_key_count_per_day,</if>
      <if test="maxPayMoneyPerDay != null">_max_pay_money_per_day,</if>
      <if test="startSubmitTime != null">_start_submit_time,</if>
      <if test="endSubmitTime != null">_end_submit_time,</if>
      <if test="creditEnable != null">_credit_enable,</if> <!-- 新增字段 -->
      <if test="createTime != null">_create_time,</if>
      <if test="updateTime != null">_update_time,</if>
    </trim>values
    </trim>
    values
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">#{id,jdbcType=BIGINT},</if>
      <if test="maxKeyCountPerDay != null">#{maxKeyCountPerDay,jdbcType=BIGINT},</if>
      <if test="maxPayMoneyPerDay != null">#{maxPayMoneyPerDay,jdbcType=DECIMAL},</if>
      <if test="startSubmitTime != null">#{startSubmitTime,jdbcType=VARCHAR},</if>
      <if test="endSubmitTime != null">#{endSubmitTime,jdbcType=VARCHAR},</if>
      <if test="creditEnable != null">#{creditEnable,jdbcType=BOOLEAN},</if> <!-- 新增字段 -->
      <if test="createTime != null">#{createTime,jdbcType=TIMESTAMP},</if>
      <if test="updateTime != null">#{updateTime,jdbcType=TIMESTAMP},</if>
    </trim>
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings">update table_agent_settings set _max_key_count_per_day = #{maxKeyCountPerDay,jdbcType=BIGINT},_max_pay_money_per_day = #{maxPayMoneyPerDay,jdbcType=DECIMAL},_start_submit_time = #{startSubmitTime,jdbcType=VARCHAR},_end_submit_time = #{endSubmitTime,jdbcType=VARCHAR},_create_time = #{createTime,jdbcType=TIMESTAMP},_update_time = #{updateTime,jdbcType=TIMESTAMP} where _id = #{id,jdbcType=BIGINT}</update>
  <update id="updateByPrimaryKeySelective" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings">update table_agent_settings
  <update id="updateByPrimaryKey" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings">
    update table_agent_settings set
    _max_key_count_per_day = #{maxKeyCountPerDay,jdbcType=BIGINT},
    _max_pay_money_per_day = #{maxPayMoneyPerDay,jdbcType=DECIMAL},
    _start_submit_time = #{startSubmitTime,jdbcType=VARCHAR},
    _end_submit_time = #{endSubmitTime,jdbcType=VARCHAR},
    _credit_enable = #{creditEnable,jdbcType=BOOLEAN}, <!-- 新增字段 -->
    _create_time = #{createTime,jdbcType=TIMESTAMP},
    _update_time = #{updateTime,jdbcType=TIMESTAMP}
    where _id = #{id,jdbcType=BIGINT}
  </update>
  <update id="updateByPrimaryKeySelective" parameterType="com.taoke.autopay.entity.agent.ChannelAgentSettings">
    update table_agent_settings
    <set>
      <if test="maxKeyCountPerDay != null">_max_key_count_per_day=#{maxKeyCountPerDay,jdbcType=BIGINT},</if>
      <if test="maxPayMoneyPerDay != null">_max_pay_money_per_day=#{maxPayMoneyPerDay,jdbcType=DECIMAL},</if>
      <if test="startSubmitTime != null">_start_submit_time=#{startSubmitTime,jdbcType=VARCHAR},</if>
      <if test="endSubmitTime != null">_end_submit_time=#{endSubmitTime,jdbcType=VARCHAR},</if>
      <if test="creditEnable != null">_credit_enable=#{creditEnable,jdbcType=BOOLEAN},</if> <!-- 新增字段 -->
      <if test="createTime != null">_create_time=#{createTime,jdbcType=TIMESTAMP},</if>
      <if test="updateTime != null">_update_time=#{updateTime,jdbcType=TIMESTAMP},</if>
    </set> where _id = #{id,jdbcType=BIGINT}
    </set>
    where _id = #{id,jdbcType=BIGINT}
  </update>
  <select id="listByIds" resultMap="BaseResultMap">select
src/main/resources/static/admin/agent-update.html
@@ -149,6 +149,14 @@
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">是否开启积分:</label>
                <div class="layui-input-inline">
                    <input type="checkbox" name="creditEnable" required lay-verify=""  autocomplete="off" class="layui-input">
                    <div class="layui-form-mid layui-word-aux">队员是否可得积分</div>
                </div>
            </div>
            <div class="layui-input-block">
                <button class="layui-btn layui-btn-normal" lay-submit lay-filter="sure" id="sure">确定</button>
            </div>
src/main/resources/static/admin/credit-setting-edit.html
New file
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>修改积分设置</title>
    <link rel="stylesheet" type="text/css" href="layui/css/layui.css" />
    <script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
    <form class="layui-form" lay-filter="edit">
        <div class="layui-form-item">
            <label class="layui-form-label">类型</label>
            <div class="layui-input-block">
                <input type="text" name="type" required lay-verify="required" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">名称</label>
            <div class="layui-input-block">
                <input type="text" name="name" required lay-verify="required" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">值</label>
            <div class="layui-input-block">
                <input type="text" name="value" required lay-verify="required" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">开始时间</label>
            <div class="layui-input-block">
                <input type="text" name="startTime" required lay-verify="required" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">结束时间</label>
            <div class="layui-input-block">
                <input type="text" name="endTime" required lay-verify="required" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <div class="layui-input-block">
                <button class="layui-btn" lay-submit lay-filter="submit">提交</button>
            </div>
        </div>
    </form>
    <script>
        layui.use(['form', 'laydate'], function () {
            var form = layui.form;
            var laydate = layui.laydate;
            // 初始化日期选择器
            laydate.render({ elem: 'input[name=startTime]' });
            laydate.render({ elem: 'input[name=endTime]' });
            // 加载数据
            var id = getUrlParam('id');
            $.get('/admin/api/credit/setting/get', { id: id }, function (res) {
                if (res.code === 0) {
                    form.val('edit', res.data);
                } else {
                    layer.msg('加载数据失败:' + res.msg);
                }
            });
            // 提交表单
            form.on('submit(submit)', function (data) {
                $.post('/admin/api/credit/setting/update', data.field, function (res) {
                    if (res.code === 0) {
                        layer.msg('修改成功');
                        parent.layer.close(parent.layer.getFrameIndex(window.name));
                    } else {
                        layer.msg('修改失败:' + res.msg);
                    }
                });
                return false;
            });
        });
        // 获取URL参数
        function getUrlParam(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return decodeURIComponent(r[2]);
            return null;
        }
    </script>
</body>
</html>
src/main/resources/static/admin/credit-setting-list.html
New file
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>积分设置管理</title>
    <link rel="stylesheet" type="text/css" href="layui/css/layui.css" />
    <script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
    <table id="table" lay-filter="table"></table>
    <script type="text/html" id="toolbar">
        <div class="layui-btn-container">
            <button class="layui-btn layui-btn-sm" lay-event="add">添加</button>
        </div>
    </script>
    <script type="text/html" id="bar">
        <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
        <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete">删除</a>
    </script>
    <script>
        layui.use(['table', 'layer'], function () {
            var table = layui.table;
            var layer = layui.layer;
            // 渲染表格
            table.render({
                elem: '#table',
                url: '/admin/api/credit/setting/list',
                toolbar: '#toolbar',
                cols: [[
                    { field: 'id', title: 'ID', width: 80 },
                    { field: 'type', title: '类型', width: 120 },
                    { field: 'name', title: '名称', width: 150 },
                    { field: 'value', title: '值', width: 150 },
                    { field: 'startTime', title: '开始时间', width: 180 },
                    { field: 'endTime', title: '结束时间', width: 180 },
                    { fixed: 'right', title: '操作', toolbar: '#bar', width: 150 }
                ]],
                page: true
            });
            // 工具栏事件
            table.on('toolbar(table)', function (obj) {
                if (obj.event === 'add') {
                    layer.open({
                        type: 2,
                        title: '添加积分设置',
                        area: ['600px', '400px'],
                        content: 'credit-setting-add.html',
                        btn: ['确定', '取消'],
                        yes: function (index, layero) {
                            var iframeWin = window[layero.find('iframe')[0]['name']];
                            iframeWin.submitForm();
                        }
                    });
                }
            });
            // 行操作事件
            table.on('tool(table)', function (obj) {
                var data = obj.data;
                if (obj.event === 'edit') {
                    layer.open({
                        type: 2,
                        title: '修改积分设置',
                        area: ['600px', '400px'],
                        content: 'credit-setting-edit.html?id=' + data.id,
                        btn: ['确定', '取消'],
                        yes: function (index, layero) {
                            var iframeWin = window[layero.find('iframe')[0]['name']];
                            iframeWin.submitForm();
                        }
                    });
                } else if (obj.event === 'delete') {
                    layer.confirm('确定删除该积分设置吗?', function (index) {
                        $.post('/admin/api/credit/setting/delete', { id: data.id }, function (res) {
                            if (res.code === 0) {
                                layer.msg('删除成功');
                                table.reload('table');
                            } else {
                                layer.msg('删除失败:' + res.msg);
                            }
                        });
                    });
                }
            });
        });
    </script>
</body>
</html>
src/main/resources/static/admin/credit/credit-exchange-record-list.html
New file
@@ -0,0 +1,152 @@
<!DOCTYPE html>
<html>
<head>
    <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>
    <link rel="stylesheet" type="text/css" href="layui/css/layui.css"/>
    <link rel="stylesheet" type="text/css" href="css/admin.css"/>
</head>
<body>
<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="uid" id="uid" placeholder="用户ID" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <select name="status" id="status">
                    <option value="">全部状态</option>
                    <option value="0">未审核</option>
                    <option value="1">已通过</option>
                    <option value="2">已拒绝</option>
                </select>
            </div>
            <div class="layui-inline">
                <input type="text" name="minCreateTime" id="minCreateTime" placeholder="开始日期" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <input type="text" name="maxCreateTime" id="maxCreateTime" placeholder="结束日期" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i
                        class="layui-icon layui-icon-search"></i>搜索
                </button>
            </div>
            <div class="layui-inline">
                <button class="layui-btn layui-btn-warm" id="approve-btn">批量通过</button>
            </div>
            <div class="layui-inline">
                <button class="layui-btn layui-btn-danger" id="reject-btn">批量驳回</button>
            </div>
        </div>
    </form>
    <div class="layui-form" id="table-list">
        <table class="layui-table" lay-even lay-skin="nob" id="table">
        </table>
    </div>
</div>
<script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/http_api.js"></script>
<script>
    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: '#minCreateTime', type: 'date'});
        laydate.render({elem: '#maxCreateTime', type: 'date'});
        let table_option = {
            elem: '#table',
            url: '/admin/api/credit/exchange-record/list', // 数据接口
            parseData: function (res) {
                return {
                    "code": res.code, // 解析接口状态
                    "msg": res.msg, // 解析提示文本
                    "count": res.count, // 解析数据长度
                    "data": res.data // 解析数据列表
                };
            },
            page: true, // 开启分页
            cols: [
                [
                    {field: 'id', title: 'ID', width: 100, fixed: 'left'},
                    {field: 'uid', title: '用户ID', width: 150},
                    {field: 'exchangeType', title: '兑换类型', width: 150},
                    {field: 'exchangeValue', title: '兑换数值', width: 150},
                    {field: 'consumedCredits', title: '消耗积分', width: 150},
                    {field: 'creditBalance', title: '积分余额', width: 150},
                    {field: 'status', title: '状态', width: 150, templet: function (d) {
                        return d.status === 0 ? '未审核' : (d.status === 1 ? '已通过' : '已拒绝');
                    }},
                    {field: 'createTime', title: '创建时间', width: 180}
                ]
            ]
        };
        // 初始化表格
        let tableIns = table.render(table_option);
        // 监听搜索
        form.on('submit(search)', function (data) {
            tableIns.reload({
                where: data.field,
                page: {curr: 1}
            });
            return false;
        });
        // 批量通过
        $('#approve-btn').on('click', function () {
            let checkStatus = table.checkStatus('table');
            let ids = checkStatus.data.map(item => item.id);
            if (ids.length === 0) {
                layer.msg("请至少选择一条记录");
                return;
            }
            $.post('/admin/api/credit/exchange-record/approve', {ids: ids}, function (response) {
                if (response.code === 0) {
                    layer.msg(response.msg);
                    tableIns.reload();
                } else {
                    layer.msg(response.msg);
                }
            }, 'json').fail(function () {
                layer.msg("网络请求失败");
            });
        });
        // 批量驳回
        $('#reject-btn').on('click', function () {
            let checkStatus = table.checkStatus('table');
            let ids = checkStatus.data.map(item => item.id);
            if (ids.length === 0) {
                layer.msg("请至少选择一条记录");
                return;
            }
            $.post('/admin/api/credit/exchange-record/reject', {ids: ids}, function (response) {
                if (response.code === 0) {
                    layer.msg(response.msg);
                    tableIns.reload();
                } else {
                    layer.msg(response.msg);
                }
            }, 'json').fail(function () {
                layer.msg("网络请求失败");
            });
        });
    });
</script>
</body>
</html>
src/main/resources/static/admin/credit/credit-record-list.html
New file
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
    <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>
    <link rel="stylesheet" type="text/css" href="layui/css/layui.css"/>
    <link rel="stylesheet" type="text/css" href="css/admin.css"/>
</head>
<body>
<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="uid" id="uid" placeholder="用户ID" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <select name="direction" id="direction">
                    <option value="">全部类型</option>
                    <option value="1">获得</option>
                    <option value="0">消耗</option>
                </select>
            </div>
            <div class="layui-inline">
                <input type="text" name="minCreateTime" id="minCreateTime" placeholder="开始日期" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <input type="text" name="maxCreateTime" id="maxCreateTime" placeholder="结束日期" autocomplete="off" class="layui-input">
            </div>
            <div class="layui-inline">
                <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i
                        class="layui-icon layui-icon-search"></i>搜索
                </button>
            </div>
        </div>
    </form>
    <div class="layui-form" id="table-list">
        <table class="layui-table" lay-even lay-skin="nob" id="table">
        </table>
    </div>
</div>
<script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/http_api.js"></script>
<script>
    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: '#minCreateTime', type: 'date'});
        laydate.render({elem: '#maxCreateTime', type: 'date'});
        let table_option = {
            elem: '#table',
            url: '/admin/api/credit/record/list', // 数据接口
            parseData: function (res) {
                return {
                    "code": res.code, // 解析接口状态
                    "msg": res.msg, // 解析提示文本
                    "count": res.count, // 解析数据长度
                    "data": res.data // 解析数据列表
                };
            },
            page: true, // 开启分页
            cols: [
                [
                    {field: 'id', title: 'ID', width: 100, fixed: 'left'},
                    {field: 'uid', title: '用户ID', width: 150},
                    {field: 'direction', title: '类型', width: 150, templet: function (d) {
                        return d.direction === 1 ? '获得' : '消耗';
                    }},
                    {field: 'creditAmount', title: '积分数量', width: 150},
                    {field: 'description', title: '说明', width: 200},
                    {field: 'createTime', title: '创建时间', width: 180}
                ]
            ]
        };
        // 初始化表格
        let tableIns = table.render(table_option);
        // 监听搜索
        form.on('submit(search)', function (data) {
            tableIns.reload({
                where: data.field,
                page: {curr: 1}
            });
            return false;
        });
    });
</script>
</body>
</html>
src/main/resources/static/admin/credit/exchange-rate-list.html
New file
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
    <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>
    <link rel="stylesheet" type="text/css" href="layui/css/layui.css"/>
    <link rel="stylesheet" type="text/css" href="css/admin.css"/>
</head>
<body>
<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="按汇率类型搜索" autocomplete="off"
                       class="layui-input">
            </div>
            <div class="layui-inline">
                <button class="layui-btn layui-btn-normal" lay-submit lay-filter="search" id="search"><i
                        class="layui-icon layui-icon-search"></i>搜索
                </button>
            </div>
        </div>
    </form>
    <div class="layui-form" id="table-list">
        <table class="layui-table" lay-even lay-skin="nob" id="table">
        </table>
    </div>
</div>
<script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/http_api.js"></script>
<script>
    layui.use(['form', 'jquery', 'layer', 'table', 'laydate'], function () {
        var table = layui.table;
        var form = layui.form;
        var $ = layui.jquery;
        let table_option = {
            elem: '#table',
            url: '/admin/api/credit/exchangeRate/list', // 数据接口
            parseData: function (res) {
                return {
                    "code": res.code, // 解析接口状态
                    "msg": res.msg, // 解析提示文本
                    "count": res.count, // 解析数据长度
                    "data": res.data // 解析数据列表
                };
            },
            page: true, // 开启分页
            cols: [
                [
                    {field: 'id', title: 'ID', width: 100, fixed: 'left'},
                    {field: 'exchangeType', title: '兑换类型', width: 150},
                    {field: 'rate', title: '汇率', width: 150},
                    {field: 'startTime', title: '开始时间', width: 180},
                    {field: 'endTime', title: '结束时间', width: 180},
                    {
                        field: '', title: '操作', width: 150, templet: function (d) {
                            return '<a href="javascript:void(0);" onclick="editExchangeRate(' + d.id + ')">修改</a>';
                        }
                    }
                ]
            ]
        };
        // 初始化表格
        let tableIns = table.render(table_option);
        // 监听搜索
        form.on('submit(search)', function (data) {
            tableIns.reload({
                where: data.field,
                page: {curr: 1}
            });
            return false;
        });
        // 编辑积分汇率
        function editExchangeRate(id) {
            layer.open({
                title: "修改积分汇率",
                type: 2,
                area: ['800px', '600px'],
                shade: 0.3,
                content: 'exchange-rate-update.html?id=' + id,
                btn: ['确定', '取消'],
                yes: function (index) {
                    window["layui-layer-iframe" + index].submit(function (res) {
                        try {
                            var loadIndex = layer.load(1);
                            $.post("/admin/api/credit/exchangeRate/update", res, function (response) {
                                layer.close(loadIndex);
                                if (response.code == 0) {
                                    layer.close(index);
                                    layer.msg("修改成功");
                                    tableIns.reload();
                                } else {
                                    layer.msg(response.msg);
                                }
                            }, 'json').fail(function () {
                                layer.close(loadIndex);
                                layer.msg("网络请求失败");
                            });
                        } catch (e) {
                            console.error(e);
                        }
                    });
                }
            });
        }
    });
</script>
</body>
</html>
src/main/resources/static/admin/credit/exchange-rate-update.html
New file
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <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"/>
</head>
<body>
<form class="layui-form" lay-filter="edit">
    <input type="hidden" name="id"/>
    <div class="layui-form-item">
        <label class="layui-form-label">兑换类型:</label>
        <div class="layui-input-inline">
            <select name="exchangeType" required lay-verify="required">
                <option value="NEW_USER_EXCHANGE">新人兑换</option>
                <option value="GENERAL_EXCHANGE">一般兑换</option>
            </select>
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">汇率:</label>
        <div class="layui-input-inline">
            <input type="text" name="rate" required lay-verify="required|money" placeholder="请输入汇率" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">开始时间:</label>
        <div class="layui-input-inline">
            <input type="text" name="startTime" required lay-verify="required" placeholder="请选择开始时间" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <label class="layui-form-label">结束时间:</label>
        <div class="layui-input-inline">
            <input type="text" name="endTime" required lay-verify="required" placeholder="请选择结束时间" autocomplete="off"
                   class="layui-input">
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
            <button class="layui-btn layui-btn-normal" lay-submit lay-filter="sure">确定</button>
        </div>
    </div>
</form>
<script src="layui/layui.js" type="text/javascript" charset="utf-8"></script>
<script src="js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/http_api.js"></script>
<script>
    layui.use(['form', 'laydate'], function () {
        var form = layui.form;
        var laydate = layui.laydate;
        // 初始化日期选择器
        laydate.render({elem: 'input[name=startTime]', type: 'datetime'});
        laydate.render({elem: 'input[name=endTime]', type: 'datetime'});
        // 获取当前ID并填充表单
        let id = http_util.getQueryString("id");
        if (id) {
            $.post("/admin/api/credit/exchangeRate/getDetail", {id: id}, function (response) {
                if (response.code === 0) {
                    form.val("edit", response.data);
                } else {
                    layer.msg(response.msg);
                }
            }, 'json').fail(function () {
                layer.msg("网络请求失败");
            });
        }
        // 提交表单
        form.on('submit(sure)', function (data) {
            parent.layer.getFrameIndex(window.name); // 先得到当前iframe层的索引
            parent.layer.closeAll(); // 再执行关闭
            parent.location.reload(); // 刷新父页面
            return false;
        });
    });
</script>
</body>
</html>
src/main/resources/static/credit/index.html
@@ -15,123 +15,113 @@
    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    <style>
        body {
            background-color: #f5f5f5;
            background-color: #FFFFFF;
            font-family: Arial, sans-serif;
            font-size: 0.3rem; /* 修改字体大小为原来的30% */
        }
        .header {
            background-color: #fff;
            padding: 1rem;
            text-align: center;
        }
        .balance {
            font-size: 1.2rem;
            color: #ff5722;
            margin-bottom: 0.5rem;
        }
        .tabs {
            display: flex;
            justify-content: space-around;
            background-color: #fff;
            margin-top: 1rem;
        }
        .tab-item {
            padding: 0.8rem 0;
            font-size: 0.9rem;
            color: #333;
        }
        .tab-item.active {
            color: #ff5722;
            border-bottom: 2px solid #ff5722;
        }
        .content {
            margin-top: 1rem;
            background-color: #fff;
        }
        .record-item {
            padding: 1rem;
            border-bottom: 1px solid #eee;
        }
        .record-title {
            font-size: 0.9rem;
            color: #333;
        }
        .record-info {
            font-size: 0.8rem;
            color: #666;
            margin-top: 0.3rem;
        }
        .alipay-container {
            padding: 1rem;
            background-color: #fff;
            margin-top: 1rem;
        }
        .alipay-title {
            font-size: 0.9rem;
            color: #333;
        .balance {
            font-size: 0.6rem; /* 修改字体大小为原来的30% */
            color: #ff5722;
            margin-bottom: 0.5rem;
        }
        .alipay-info {
            font-size: 0.8rem;
            color: #666;
        .estimated-amount {
            font-size: 0.21rem; /* 修改字体大小为原来的30% */
            color: #333;
            margin-top: -0.5rem;
        }
        .exchange-container {
            text-align: center;
            margin: 1rem 0;
            padding: 1rem;
            background-color: #fff;
            border-radius: 0.2rem;
        }
        .exchange-title {
            font-size: 0.6rem; /* 修改字体大小为原来的30% */
            color: #333;
            margin-top: 0.8rem;
            margin-bottom: 1rem;
        }
        .exchange-input {
            display: flex;
            justify-content: center;
            align-items: center;
            margin-bottom: 1rem;
        }
        .exchange-input input {
            width: 60%;
            padding: 0.2rem;
            border: 1px solid #ddd;
            border-radius: 0.2rem;
            font-size: 0.3rem; /* 修改字体大小为原来的30% */
            text-align: center;
        }
        .btn {
            background-color: #ff5722;
            color: #fff;
            border: none;
            padding: 0.6rem 1rem;
            border-radius: 0.3rem;
            font-size: 0.9rem;
            padding: 0.2rem 0.3rem;
            border-radius: 0.2rem;
            font-size: 0.3rem;
            margin-left: 0.3rem;
            cursor: pointer;
        }
        .btn:hover {
            background-color: #e64a19;
        }
        .links-container {
            text-align: center;
            margin-top: 1rem;
        }
        .links-container a {
            display: block;
            color: #ff5722;
            text-decoration: none;
            margin: 0.2rem 0;
            font-size: 0.3rem; /* 修改字体大小为原来的30% */
        }
        .links-container a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
<div id="app">
    <div class="header">
        <div class="balance">当前积分余额: {{ creditBalance }}</div>
    </div>
    <div class="exchange-container">
        <div class="balance"><span style=" color:black;">当前积分余额: </span>{{ creditBalance }}</div>
        <div class="estimated-amount">预计可兑换红包金额: {{ estimatedRedPacketAmount }}元</div>
    <div class="tabs">
        <div class="tab-item" :class="{ active: activeTab === 'records' }" @click="activeTab = 'records'">积分记录</div>
        <div class="tab-item" :class="{ active: activeTab === 'exchanges' }" @click="activeTab = 'exchanges'">兑换记录</div>
    </div>
        <div class="exchange-title">积分红包兑换</div>
    <div class="content" v-if="activeTab === 'records'">
        <div class="record-item" v-for="record in creditRecords">
            <div class="record-title">{{ record.description }}</div>
            <div class="record-info">{{ record.amount }}积分 - {{ record.createTime }}</div>
        </div>
    </div>
    <div class="content" v-if="activeTab === 'exchanges'">
        <div class="record-item" v-for="exchange in exchangeRecords">
            <div class="record-title">{{ exchange.exchangeType }}</div>
            <div class="record-info">{{ exchange.exchangeValue }} - {{ exchange.createTime }}</div>
        <div class="exchange-input">
            <input type="number" v-model="exchangePoints" placeholder="请输入兑换积分" />
            <button class="btn" @click="exchangePointsToRedPacket">兑换</button>
        </div>
    </div>
    <div class="alipay-container">
        <div class="alipay-title">支付宝绑定</div>
        <div class="alipay-info" v-if="alipayBinding">
            {{ alipayBinding.alipayName }} - {{ alipayBinding.alipayAccount }}
        <div class="links-container">
            <a href="alipay_account_setting.html">绑定支付宝</a>
            <a href="credit_records.html">查看积分记录</a>
            <a href="exchange_records.html">查看兑换记录</a>
        </div>
        <div class="alipay-info" v-else>
            未绑定支付宝
        </div>
        <button class="btn" @click="bindAlipay">绑定支付宝</button>
    </div>
</div>
@@ -143,49 +133,41 @@
        el: '#app',
        data: {
            creditBalance: 0,
            creditRecords: [],
            exchangeRecords: [],
            alipayBinding: null,
            activeTab: 'records'
            estimatedRedPacketAmount: 0,
            exchangePoints: ''
        },
        mounted() {
            this.loadCreditBalance();
            this.loadCreditRecords();
            this.loadExchangeRecords();
            this.loadAlipayBinding();
        },
        methods: {
            loadCreditBalance() {
                // 调用API获取积分余额
                http.get('/api/credit/balance').then(response => {
                    this.creditBalance = response.data.balance;
                    this.calculateEstimatedRedPacketAmount();
                });
            },
            loadCreditRecords() {
                // 调用API获取积分记录
                http.get('/api/credit/records').then(response => {
                    this.creditRecords = response.data.records;
                });
            calculateEstimatedRedPacketAmount() {
                // 假设每100积分可兑换1元红包
                this.estimatedRedPacketAmount = (this.creditBalance / 100).toFixed(2);
            },
            loadExchangeRecords() {
                // 调用API获取兑换记录
                http.get('/api/credit/exchanges').then(response => {
                    this.exchangeRecords = response.data.records;
            exchangePointsToRedPacket() {
                if (!this.exchangePoints || this.exchangePoints <= 0) {
                    alert('请输入有效的兑换积分');
                    return;
                }
                // 调用API进行积分兑换
                http.post('/api/credit/exchange', { points: this.exchangePoints }).then(response => {
                    alert('兑换成功');
                    this.loadCreditBalance();
                    this.exchangePoints = '';
                }).catch(error => {
                    alert('兑换失败: ' + error.message);
                });
            },
            loadAlipayBinding() {
                // 调用API获取支付宝绑定信息
                http.get('/api/alipay/binding').then(response => {
                    this.alipayBinding = response.data.binding;
                });
            },
            bindAlipay() {
                // 跳转到支付宝绑定页面
                window.location.href = '../agent/alipay_account_setting.html';
            }
        }
    });
</script>
</body>
</html>
</html>