202507041738
This commit is contained in:
parent
9badb89994
commit
faf16add2c
|
|
@ -13,10 +13,21 @@ ruoyi:
|
||||||
# 验证码类型 math 数字计算 char 字符验证
|
# 验证码类型 math 数字计算 char 字符验证
|
||||||
captchaType: math
|
captchaType: math
|
||||||
wechat:
|
wechat:
|
||||||
appid: wx73d0202b3c8a6d68
|
appid: wx1234567890123456
|
||||||
mchid: 1672571923
|
mchid: 1672571923
|
||||||
apikey: sssssssssssssssssssssssssssssssS
|
apikey: sssssssssssssssssssssssssssssssS
|
||||||
certpath: wechat/apiclient_cert.p12
|
certpath: wechat/apiclient_cert.p12
|
||||||
|
apiv3-key: sssssssssssssssssssssssssssssssS
|
||||||
|
serial-no: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||||
|
private-key-path: wechat/apiclient_key.pem
|
||||||
|
cert-dir: wechat
|
||||||
|
# 微信支付平台证书序列号(临时使用商户证书序列号进行测试)
|
||||||
|
wechatpay-serial: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||||
|
|
||||||
|
# appid: wx73d0202b3c8a6d68
|
||||||
|
# mchid: 1672571923
|
||||||
|
# apikey: sssssssssssssssssssssssssssssssS
|
||||||
|
# certpath: wechat/apiclient_cert.p12
|
||||||
# 七牛云配置
|
# 七牛云配置
|
||||||
qiniu:
|
qiniu:
|
||||||
# 是否启用七牛云上传 true-启用七牛云 false-使用本地上传
|
# 是否启用七牛云上传 true-启用七牛云 false-使用本地上传
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,15 @@ public class WechatConfig {
|
||||||
private String apikey;
|
private String apikey;
|
||||||
private String certpath;
|
private String certpath;
|
||||||
|
|
||||||
|
// 微信支付V3新增配置
|
||||||
|
private String apiv3Key;
|
||||||
|
private String privateKeyPath;
|
||||||
|
private String serialNo;
|
||||||
|
private String certDir;
|
||||||
|
|
||||||
|
// 微信支付平台证书序列号(新增)
|
||||||
|
private String wechatpaySerial;
|
||||||
|
|
||||||
public String getAppid() {
|
public String getAppid() {
|
||||||
return appid;
|
return appid;
|
||||||
}
|
}
|
||||||
|
|
@ -43,4 +52,44 @@ public class WechatConfig {
|
||||||
public void setCertpath(String certpath) {
|
public void setCertpath(String certpath) {
|
||||||
this.certpath = certpath;
|
this.certpath = certpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getApiv3Key() {
|
||||||
|
return apiv3Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApiv3Key(String apiv3Key) {
|
||||||
|
this.apiv3Key = apiv3Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrivateKeyPath() {
|
||||||
|
return privateKeyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrivateKeyPath(String privateKeyPath) {
|
||||||
|
this.privateKeyPath = privateKeyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSerialNo() {
|
||||||
|
return serialNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSerialNo(String serialNo) {
|
||||||
|
this.serialNo = serialNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCertDir() {
|
||||||
|
return certDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCertDir(String certDir) {
|
||||||
|
this.certDir = certDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWechatpaySerial() {
|
||||||
|
return wechatpaySerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWechatpaySerial(String wechatpaySerial) {
|
||||||
|
this.wechatpaySerial = wechatpaySerial;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.ruoyi.system.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.system.ControllerUtil.WechatPayV3Util;
|
||||||
|
import com.ruoyi.system.service.WechatCertificateService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付API控制器(无需认证)
|
||||||
|
*
|
||||||
|
* @author Mr. Zhang Pan
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-01-17
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/wechat/pay/v3")
|
||||||
|
public class ApiWechatPayController extends BaseController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ApiWechatPayController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatPayV3Util wechatPayV3Util;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatCertificateService wechatCertificateService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速提现(简化参数)
|
||||||
|
*/
|
||||||
|
@PostMapping("/quick-withdraw")
|
||||||
|
@Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult quickWithdraw(@RequestParam String openid,
|
||||||
|
@RequestParam BigDecimal amount,
|
||||||
|
@RequestParam(required = false) String desc) {
|
||||||
|
try {
|
||||||
|
log.info("⚡ API快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("快速提现成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ API快速提现异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("快速提现失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试私钥加载
|
||||||
|
*/
|
||||||
|
@GetMapping("/test-key")
|
||||||
|
@Log(title = "微信支付V3测试私钥", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult testKey() {
|
||||||
|
try {
|
||||||
|
log.info("🔐 API测试私钥加载");
|
||||||
|
|
||||||
|
// 通过反射调用私有方法测试私钥加载
|
||||||
|
java.lang.reflect.Method method = WechatPayV3Util.class.getDeclaredMethod("getPrivateKey");
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object privateKey = method.invoke(wechatPayV3Util);
|
||||||
|
|
||||||
|
if (privateKey != null) {
|
||||||
|
return AjaxResult.success("私钥加载成功", "私钥算法: " + privateKey.getClass().getSimpleName());
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error("私钥加载失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ API测试私钥异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("测试私钥失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,11 @@ package com.ruoyi.system.controller;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.system.ControllerUtil.*;
|
import com.ruoyi.system.ControllerUtil.*;
|
||||||
import com.ruoyi.system.domain.*;
|
import com.ruoyi.system.domain.*;
|
||||||
|
|
@ -35,18 +37,26 @@ import com.ruoyi.system.domain.QuoteMaterial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序控制器
|
* 小程序控制器
|
||||||
* <p>
|
*
|
||||||
* 提供小程序端所需的API接口
|
* 提供小程序端所需的API接口,包括:
|
||||||
* 主要功能:
|
* 1. 服务分类管理 - 获取服务分类列表
|
||||||
* 1. 服务分类管理
|
* 2. 服务商品管理 - 商品列表、详情、搜索
|
||||||
* 2. 服务商品列表和详情
|
* 3. 广告图片管理 - 获取各类型广告图片
|
||||||
* 3. 广告图片获取
|
* 4. 配置信息管理 - 系统配置查询
|
||||||
* 4. 配置信息查询
|
* 5. 用户信息管理 - 登录验证、信息修改
|
||||||
* 5. 用户信息验证
|
* 6. 地址管理 - 收货地址增删改查
|
||||||
|
* 7. 订单管理 - 服务订单、商品订单
|
||||||
|
* 8. 购物车管理 - 商品购物车操作
|
||||||
|
* 9. 支付相关 - 微信支付、订单支付
|
||||||
|
* 10. 师傅端功能 - 接单、报价、服务流程
|
||||||
|
* 11. 会员相关 - 充值、消费记录
|
||||||
|
* 12. 积分商城 - 积分商品兑换
|
||||||
|
* 13. 优惠券管理 - 用户优惠券
|
||||||
|
* 14. 售后服务 - 返修申请
|
||||||
*
|
*
|
||||||
* @author Mr. Zhang Pan
|
* @author Mr. Zhang Pan
|
||||||
* @version 1.0
|
* @version 2.0
|
||||||
* @date 2025-05-26
|
* @date 2025-01-17
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
public class AppletController extends BaseController {
|
public class AppletController extends BaseController {
|
||||||
|
|
@ -134,34 +144,91 @@ public class AppletController extends BaseController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IUserSecondaryCardService userSecondaryCardService;
|
private IUserSecondaryCardService userSecondaryCardService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserDemandQuotationService userDemandQuotationService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserBenefitPointsService userBenefitPointsService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatPayV3Util wechatPayV3Util;
|
||||||
|
@PostMapping("/api/quick-refund")
|
||||||
|
public AjaxResult quickRefund(
|
||||||
|
@RequestParam String orderNo,
|
||||||
|
@RequestParam BigDecimal refundFee,
|
||||||
|
@RequestParam(required = false) String reason) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Map<String, Object> result = wechatPayV3Util.quickRefund(orderNo, refundFee, reason);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return success(result.get("data"));
|
||||||
|
} else {
|
||||||
|
return error((String) result.get("message"));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return error("快速退款异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户提现
|
||||||
|
*
|
||||||
|
* @param openid 用户openid
|
||||||
|
* @param amount 金额(分)
|
||||||
|
* @param desc 提现描述
|
||||||
|
* @param userName 真实姓名(可选,用于实名校验)
|
||||||
|
* @return 提现结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/api/withdraw")
|
||||||
|
public AjaxResult withdraw(@RequestParam String openid,
|
||||||
|
@RequestParam int amount,
|
||||||
|
@RequestParam(required = false) String desc,
|
||||||
|
@RequestParam(required = false) String userName) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("提现申请成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
return AjaxResult.error("提现申请失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务分类列表
|
* 获取服务分类列表
|
||||||
*
|
*
|
||||||
* @param request HTTP请求对象
|
* 功能说明:
|
||||||
* @return 分类列表数据
|
|
||||||
* <p>
|
|
||||||
* 接口说明:
|
|
||||||
* - 获取状态为启用的服务分类
|
* - 获取状态为启用的服务分类
|
||||||
* - 自动添加图片CDN前缀
|
* - 自动添加图片CDN前缀
|
||||||
* - 支持用户登录状态验证
|
* - 支持用户登录状态验证(可选)
|
||||||
|
*
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @return 分类列表数据
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/api/service/cate")
|
@GetMapping(value = "/api/service/cate")
|
||||||
public AjaxResult getInfo(HttpServletRequest request) {
|
public AjaxResult getServiceCategories(HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
// 验证用户登录状态(可选)
|
// 验证用户登录状态(可选)
|
||||||
Map<String, Object> userData = AppletControllerUtil.getUserData(request.getHeader("token"), usersService);
|
AppletControllerUtil.getUserData(request.getHeader("token"), usersService);
|
||||||
|
|
||||||
// 构建查询条件:状态启用且类型为服务
|
// 查询启用状态的服务分类
|
||||||
ServiceCate serviceCateQuery = new ServiceCate();
|
ServiceCate serviceCateQuery = new ServiceCate();
|
||||||
serviceCateQuery.setStatus(1L); // 启用状态
|
serviceCateQuery.setStatus(1L);
|
||||||
// serviceCateQuery.setType(1L); // 服务类型
|
|
||||||
// 查询分类列表
|
|
||||||
List<ServiceCate> categoryList = serviceCateService.selectServiceCateList(serviceCateQuery);
|
List<ServiceCate> categoryList = serviceCateService.selectServiceCateList(serviceCateQuery);
|
||||||
|
|
||||||
// 为每个分类添加CDN前缀
|
// 为每个分类添加CDN前缀
|
||||||
for (ServiceCate category : categoryList) {
|
AppletControllerUtil.addImageCdnPrefixForCategories(categoryList);
|
||||||
category.setIcon(AppletControllerUtil.buildImageUrl(category.getIcon()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return AppletControllerUtil.appletSuccess(categoryList);
|
return AppletControllerUtil.appletSuccess(categoryList);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
@ -173,42 +240,32 @@ public class AppletController extends BaseController {
|
||||||
/**
|
/**
|
||||||
* 获取系统配置信息
|
* 获取系统配置信息
|
||||||
*
|
*
|
||||||
* @param name 配置项名称
|
* 功能说明:
|
||||||
* @param request HTTP请求对象
|
|
||||||
* @return 配置信息数据
|
|
||||||
* <p>
|
|
||||||
* 接口说明:
|
|
||||||
* - 根据配置名称获取对应的配置值
|
* - 根据配置名称获取对应的配置值
|
||||||
* - 配置值以JSON格式返回
|
* - 配置值以JSON格式返回
|
||||||
* - 支持动态配置管理
|
* - 支持动态配置管理
|
||||||
|
*
|
||||||
|
* @param name 配置项名称
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @return 配置信息数据
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/api/public/config/{name}")
|
@GetMapping(value = "/api/public/config/{name}")
|
||||||
public AjaxResult config(@PathVariable("name") String name, HttpServletRequest request) {
|
public AjaxResult getConfig(@PathVariable("name") String name, HttpServletRequest request) {
|
||||||
try {
|
try {
|
||||||
// 参数验证
|
// 参数验证
|
||||||
if (name == null || name.trim().isEmpty()) {
|
if (StringUtils.isEmpty(name)) {
|
||||||
return AppletControllerUtil.appletWarning("配置名称不能为空");
|
return AppletControllerUtil.appletWarning("配置名称不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建查询条件
|
// 查询配置信息
|
||||||
SiteConfig configQuery = new SiteConfig();
|
SiteConfig config = AppletControllerUtil.getSiteConfig(name, siteConfigService);
|
||||||
configQuery.setName(name.trim());
|
if (config == null) {
|
||||||
|
|
||||||
// 查询配置列表
|
|
||||||
List<SiteConfig> configList = siteConfigService.selectSiteConfigList(configQuery);
|
|
||||||
|
|
||||||
if (!configList.isEmpty()) {
|
|
||||||
// 解析配置值为JSON对象
|
|
||||||
String configValue = configList.get(0).getValue();
|
|
||||||
if (configValue != null && !configValue.trim().isEmpty()) {
|
|
||||||
JSONObject jsonObject = JSONObject.parseObject(configValue);
|
|
||||||
return AppletControllerUtil.appletSuccess(jsonObject);
|
|
||||||
} else {
|
|
||||||
return AppletControllerUtil.appletWarning("配置值为空");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return AppletControllerUtil.appletWarning("未找到指定的配置项:" + name);
|
return AppletControllerUtil.appletWarning("未找到指定的配置项:" + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 解析配置值为JSON对象
|
||||||
|
JSONObject configJson = AppletControllerUtil.parseConfigValue(config.getValue());
|
||||||
|
return AppletControllerUtil.appletSuccess(configJson);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return AppletControllerUtil.appletError("获取配置信息失败:" + e.getMessage());
|
return AppletControllerUtil.appletError("获取配置信息失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -6642,33 +6699,82 @@ public class AppletController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 获取新参数并校验必填
|
// 3. 获取新参数并校验必填
|
||||||
Long orderId = params.get("orderid") != null ? Long.valueOf(params.get("orderid").toString()) : null;
|
Long oid = params.get("oid") != null ? Long.valueOf(params.get("oid").toString()) : null;
|
||||||
Integer dataType = params.get("datatype") != null ? Integer.valueOf(params.get("datatype").toString()) : null;
|
Integer dataType = params.get("datatype") != null ? Integer.valueOf(params.get("datatype").toString()) : null;
|
||||||
if (orderId == null || dataType == null) {
|
if (oid == null || dataType == null) {
|
||||||
return AppletControllerUtil.appletWarning("orderid和datatype不能为空");
|
return AppletControllerUtil.appletWarning("orderid和datatype不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BigDecimal invoiceMoney = BigDecimal.ZERO;
|
BigDecimal invoiceMoney = BigDecimal.ZERO;
|
||||||
String invoiceText = "";
|
String invoiceText = "";
|
||||||
|
String orderid = "";
|
||||||
if (dataType == 1) {
|
if (dataType == 1) {
|
||||||
GoodsOrder order = goodsOrderService.selectGoodsOrderById(orderId);
|
// 商品订单:xxx商品*数量=xxx元
|
||||||
|
GoodsOrder order = goodsOrderService.selectGoodsOrderById(oid);
|
||||||
if (order != null) {
|
if (order != null) {
|
||||||
|
orderid=order.getOrderId();
|
||||||
invoiceMoney = order.getTotalPrice();
|
invoiceMoney = order.getTotalPrice();
|
||||||
invoiceText = "商品订单-" + order.getOrderId();
|
// 获取商品名称
|
||||||
|
String productName = order.getProductName();
|
||||||
|
if (productName == null || productName.trim().isEmpty()) {
|
||||||
|
// 如果订单中没有商品名称,尝试从商品表获取
|
||||||
|
ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId());
|
||||||
|
if (serviceGoods != null) {
|
||||||
|
productName = serviceGoods.getTitle();
|
||||||
|
} else {
|
||||||
|
productName = "商品";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Long quantity = order.getNum() != null ? order.getNum() : 1L;
|
||||||
|
invoiceText = productName + "*" + quantity + "=" + invoiceMoney + "元";
|
||||||
}
|
}
|
||||||
} else if (dataType == 2) {
|
} else if (dataType == 2) {
|
||||||
Order order = orderService.selectOrderById(orderId);
|
// 服务订单:xxx服务多少元
|
||||||
|
Order order = orderService.selectOrderById(oid);
|
||||||
|
|
||||||
if (order != null) {
|
if (order != null) {
|
||||||
|
orderid=order.getOrderId() ;
|
||||||
invoiceMoney = order.getTotalPrice();
|
invoiceMoney = order.getTotalPrice();
|
||||||
invoiceText = "服务订单-" + order.getOrderId();
|
// 获取服务名称
|
||||||
|
String serviceName = order.getProductName();
|
||||||
|
if (serviceName == null || serviceName.trim().isEmpty()) {
|
||||||
|
// 如果订单中没有服务名称,尝试从服务表获取
|
||||||
|
ServiceGoods serviceGoods = serviceGoodsService.selectServiceGoodsById(order.getProductId());
|
||||||
|
if (serviceGoods != null) {
|
||||||
|
serviceName = serviceGoods.getTitle();
|
||||||
|
} else {
|
||||||
|
serviceName = "服务";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invoiceText = serviceName + "服务" + invoiceMoney + "元";
|
||||||
}
|
}
|
||||||
} else if (dataType == 3) {
|
} else if (dataType == 3) {
|
||||||
UserMemberRechargeLog recharge = userMemberRechargeLogService.selectUserMemberRechargeLogById(orderId.intValue());
|
// 充值订单:xxx充值金额xxx元
|
||||||
|
UserMemberRechargeLog recharge = userMemberRechargeLogService.selectUserMemberRechargeLogById(oid.intValue());
|
||||||
if (recharge != null) {
|
if (recharge != null) {
|
||||||
|
orderid=recharge.getOrderid();
|
||||||
invoiceMoney = recharge.getInmoney();
|
invoiceMoney = recharge.getInmoney();
|
||||||
invoiceText = "充值订单-" + recharge.getInmoney();
|
String rechargeName = recharge.getReamk();
|
||||||
|
if (rechargeName == null || rechargeName.trim().isEmpty()) {
|
||||||
|
rechargeName = "会员";
|
||||||
|
}
|
||||||
|
invoiceText = rechargeName + "充值金额" + invoiceMoney + "元";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 4. 构建发票信息对象
|
|
||||||
|
|
||||||
|
// 4. 检查是否已经开过票(防止重复开票)
|
||||||
|
UsersInvoiceInfo checkQuery = new UsersInvoiceInfo();
|
||||||
|
checkQuery.setOrderid(orderid);
|
||||||
|
List<UsersInvoiceInfo> existingInvoices = usersInvoiceInfoService.selectUsersInvoiceInfoList(checkQuery);
|
||||||
|
if (existingInvoices != null && !existingInvoices.isEmpty()) {
|
||||||
|
return AppletControllerUtil.appletWarning("该订单已经开过发票,不能重复开票");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 5. 构建发票信息对象
|
||||||
UsersInvoiceInfo info = new UsersInvoiceInfo();
|
UsersInvoiceInfo info = new UsersInvoiceInfo();
|
||||||
info.setUid(user.getId().intValue());
|
info.setUid(user.getId().intValue());
|
||||||
info.setInvoiceTitle((String) params.get("invoiceTitle"));
|
info.setInvoiceTitle((String) params.get("invoiceTitle"));
|
||||||
|
|
@ -6681,11 +6787,12 @@ public class AppletController extends BaseController {
|
||||||
info.setWechat((String) params.get("wechat"));
|
info.setWechat((String) params.get("wechat"));
|
||||||
info.setType(Integer.parseInt(params.get("type").toString()));
|
info.setType(Integer.parseInt(params.get("type").toString()));
|
||||||
info.setCategory(Integer.parseInt(params.get("category").toString()));
|
info.setCategory(Integer.parseInt(params.get("category").toString()));
|
||||||
info.setOrderid(orderId.toString());
|
info.setOrderid(orderid);
|
||||||
info.setInvoicemoney(invoiceMoney);
|
info.setInvoicemoney(invoiceMoney);
|
||||||
info.setStatus(1);
|
info.setStatus(1);
|
||||||
info.setInvoicetext(invoiceText);
|
info.setInvoicetext(invoiceText);
|
||||||
// 5. 保存或更新发票信息
|
|
||||||
|
// 6. 保存或更新发票信息
|
||||||
Integer id = params.get("id") != null ? Integer.parseInt(params.get("id").toString()) : null;
|
Integer id = params.get("id") != null ? Integer.parseInt(params.get("id").toString()) : null;
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
info.setId(id);
|
info.setId(id);
|
||||||
|
|
@ -6969,6 +7076,81 @@ public AjaxResult checkUserDefault(HttpServletRequest request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 师傅报价接口
|
||||||
|
*
|
||||||
|
* @param params 包含orderid和money的参数
|
||||||
|
* @param request HTTP请求对象
|
||||||
|
* @return 报价结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/api/worker/quote/price")
|
||||||
|
public AjaxResult workerQuotePrice(@RequestBody Map<String, Object> params, HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
// 1. 验证用户登录状态
|
||||||
|
String token = request.getHeader("token");
|
||||||
|
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
|
||||||
|
if (!(Boolean) userValidation.get("valid")) {
|
||||||
|
return AppletControllerUtil.appletWarning("用户未登录或token无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取用户信息
|
||||||
|
Users user = (Users) userValidation.get("user");
|
||||||
|
if (user == null) {
|
||||||
|
return AppletControllerUtil.appletWarning("用户信息获取失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 验证必要参数
|
||||||
|
if (params == null || params.get("orderid") == null || params.get("money") == null) {
|
||||||
|
return AppletControllerUtil.appletWarning("订单ID和报价金额不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 获取参数
|
||||||
|
String orderId = params.get("orderid").toString();
|
||||||
|
BigDecimal quoteMoney = new BigDecimal(params.get("money").toString());
|
||||||
|
|
||||||
|
// 5. 查询订单是否存在
|
||||||
|
Order order = orderService.selectOrderByOrderId(orderId);
|
||||||
|
if (order == null) {
|
||||||
|
return AppletControllerUtil.appletWarning("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 查询用户是否已对该订单报过价
|
||||||
|
UserDemandQuotation queryParams = new UserDemandQuotation();
|
||||||
|
queryParams.setWorkerid(user.getId());
|
||||||
|
queryParams.setOrderid(orderId);
|
||||||
|
List<UserDemandQuotation> existingQuotes = userDemandQuotationService.selectUserDemandQuotationList(queryParams);
|
||||||
|
|
||||||
|
// 7. 处理报价
|
||||||
|
UserDemandQuotation quoteRecord;
|
||||||
|
if (existingQuotes != null && !existingQuotes.isEmpty()) {
|
||||||
|
// 已有报价,更新
|
||||||
|
quoteRecord = existingQuotes.getFirst();
|
||||||
|
quoteRecord.setMoney(quoteMoney);
|
||||||
|
quoteRecord.setQuotationTime(new Date());
|
||||||
|
quoteRecord.setUpdateTime(new Date());
|
||||||
|
userDemandQuotationService.updateUserDemandQuotation(quoteRecord);
|
||||||
|
} else {
|
||||||
|
// 新增报价
|
||||||
|
quoteRecord = new UserDemandQuotation();
|
||||||
|
quoteRecord.setWorkerid(user.getId());
|
||||||
|
quoteRecord.setOrderid(orderId);
|
||||||
|
quoteRecord.setMoney(quoteMoney);
|
||||||
|
quoteRecord.setQuotationTime(new Date());
|
||||||
|
quoteRecord.setStatus(1L); // 设置状态为有效
|
||||||
|
quoteRecord.setWorkername(user.getName());
|
||||||
|
quoteRecord.setWorkerimage(user.getAvatar());
|
||||||
|
quoteRecord.setCreateTime(new Date());
|
||||||
|
userDemandQuotationService.insertUserDemandQuotation(quoteRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppletControllerUtil.appletSuccess("报价成功");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("师傅报价异常:" + e.getMessage());
|
||||||
|
return AppletControllerUtil.appletError("报价失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("api/group/once_pay")
|
@PostMapping("api/group/once_pay")
|
||||||
public AjaxResult apigroupOncePay(@RequestBody Map<String, Object> params, HttpServletRequest request) {
|
public AjaxResult apigroupOncePay(@RequestBody Map<String, Object> params, HttpServletRequest request) {
|
||||||
// 1. 验证用户登录状态
|
// 1. 验证用户登录状态
|
||||||
|
|
@ -7025,6 +7207,68 @@ public AjaxResult checkUserDefault(HttpServletRequest request) {
|
||||||
}
|
}
|
||||||
return AppletControllerUtil.appletWarning("支付失败");
|
return AppletControllerUtil.appletWarning("支付失败");
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 小程序端:查询用户服务金/消费金日志(分页,PageUtil.buildPageResponse)
|
||||||
|
* type: 1=服务金,2=消费金
|
||||||
|
* limit: 每页条数,page: 页码
|
||||||
|
*/
|
||||||
|
@GetMapping("/api/user/benefit/log")
|
||||||
|
public AjaxResult getUserBenefitLog(
|
||||||
|
@RequestParam(value = "type") Integer type,
|
||||||
|
@RequestParam(value = "limit", defaultValue = "10") int limit,
|
||||||
|
@RequestParam(value = "page", defaultValue = "1") int page,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
try {
|
||||||
|
// 1. 验证分页参数
|
||||||
|
Map<String, Object> pageValidation = PageUtil.validatePageParams(page, limit);
|
||||||
|
if (!(Boolean) pageValidation.get("valid")) {
|
||||||
|
return AppletControllerUtil.appletWarning((String) pageValidation.get("message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 验证用户登录状态
|
||||||
|
String token = request.getHeader("token");
|
||||||
|
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
|
||||||
|
if (!(Boolean) userValidation.get("valid")) {
|
||||||
|
return AppletControllerUtil.appletUnauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 获取用户信息
|
||||||
|
Users user = (Users) userValidation.get("user");
|
||||||
|
if (user == null) {
|
||||||
|
return AppletControllerUtil.appletWarning("用户信息获取失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 设置分页参数
|
||||||
|
PageHelper.startPage(page, limit);
|
||||||
|
|
||||||
|
// 5. 查询服务金/消费金日志
|
||||||
|
UserBenefitPoints query = new UserBenefitPoints();
|
||||||
|
query.setUid(user.getId());
|
||||||
|
query.setType(Long.valueOf(type));
|
||||||
|
List<UserBenefitPoints> logList = userBenefitPointsService.selectUserBenefitPointsList(query);
|
||||||
|
|
||||||
|
// 6. 获取分页信息并构建响应
|
||||||
|
TableDataInfo tableDataInfo = getDataTable(logList);
|
||||||
|
Map<String, Object> pageData = PageUtil.buildPageResponse(tableDataInfo, page, limit);
|
||||||
|
|
||||||
|
return AppletControllerUtil.appletSuccess(pageData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("查询用户服务金/消费金日志异常:" + e.getMessage());
|
||||||
|
return AppletControllerUtil.appletError("查询服务金/消费金日志失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序用户余额日志查询接口
|
||||||
|
*/
|
||||||
|
@PostMapping("/api/user/balance/logs")
|
||||||
|
public AjaxResult getBalanceLogList(HttpServletRequest request, @RequestBody Map<String, Object> params) {
|
||||||
|
// 从请求头获取token
|
||||||
|
String token = request.getHeader("token");
|
||||||
|
|
||||||
|
// 调用工具类方法
|
||||||
|
return AppletControllerUtil.getUserBalanceLogList(params, token, usersService, userMemnerConsumptionLogService);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,11 @@ import java.util.List;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import com.ruoyi.system.domain.QuoteType;
|
import com.ruoyi.system.domain.QuoteType;
|
||||||
|
import com.ruoyi.system.domain.UserBenefitPoints;
|
||||||
|
import com.ruoyi.system.service.IUserBenefitPointsService;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
|
@ -22,6 +17,8 @@ import com.ruoyi.system.domain.Users;
|
||||||
import com.ruoyi.system.service.IUsersService;
|
import com.ruoyi.system.service.IUsersService;
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.system.domain.UserMemnerConsumptionLog;
|
||||||
|
import com.ruoyi.system.service.IUserMemnerConsumptionLogService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 【请填写功能名称】Controller
|
* 【请填写功能名称】Controller
|
||||||
|
|
@ -36,6 +33,11 @@ public class UsersController extends BaseController
|
||||||
@Autowired
|
@Autowired
|
||||||
private IUsersService usersService;
|
private IUsersService usersService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserBenefitPointsService userBenefitPointsService;
|
||||||
|
@Autowired
|
||||||
|
private IUserMemnerConsumptionLogService userMemnerConsumptionLogService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询【请填写功能名称】列表
|
* 查询【请填写功能名称】列表
|
||||||
*/
|
*/
|
||||||
|
|
@ -141,4 +143,31 @@ public class UsersController extends BaseController
|
||||||
{
|
{
|
||||||
return toAjax(usersService.deleteUsersByIds(ids));
|
return toAjax(usersService.deleteUsersByIds(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户消费金或服务金日志记录
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('system:users:query')")
|
||||||
|
@GetMapping("/benefitLogs/{userId}/{type}")
|
||||||
|
public TableDataInfo getBenefitLogs(@PathVariable("userId") Long userId, @PathVariable("type") Integer type)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
UserBenefitPoints userBenefitPoints = new UserBenefitPoints();
|
||||||
|
userBenefitPoints.setUid(userId);
|
||||||
|
userBenefitPoints.setType(Long.valueOf(type));
|
||||||
|
return getDataTable(userBenefitPointsService.selectUserBenefitPointsList(userBenefitPoints));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户余额变动日志记录
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('system:users:query')")
|
||||||
|
@GetMapping("/balanceLogs/{userId}")
|
||||||
|
public TableDataInfo getBalanceLogs(@PathVariable("userId") Long userId)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
UserMemnerConsumptionLog query = new UserMemnerConsumptionLog();
|
||||||
|
query.setUid(Math.toIntExact(userId)); //
|
||||||
|
return getDataTable(userMemnerConsumptionLogService.selectUserMemnerConsumptionLogList(query));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,405 @@
|
||||||
|
package com.ruoyi.system.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Anonymous;
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.system.ControllerUtil.WechatPayV3Util;
|
||||||
|
import com.ruoyi.system.service.WechatCertificateService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付V3控制器
|
||||||
|
*
|
||||||
|
* 提供微信支付V3 API的REST接口:
|
||||||
|
* 1. 申请退款
|
||||||
|
* 2. 查询退款
|
||||||
|
* 3. 用户提现
|
||||||
|
* 4. 查询转账
|
||||||
|
*
|
||||||
|
* @author Mr. Zhang Pan
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-01-17
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/wechat/pay/v3")
|
||||||
|
public class WechatPayV3Controller extends BaseController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WechatPayV3Controller.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatPayV3Util wechatPayV3Util;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatCertificateService wechatCertificateService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请退款
|
||||||
|
*
|
||||||
|
* @param orderNo 商户订单号
|
||||||
|
* @param refundNo 退款单号
|
||||||
|
* @param totalFee 订单总金额(分)
|
||||||
|
* @param refundFee 退款金额(分)
|
||||||
|
* @param reason 退款原因
|
||||||
|
* @param notifyUrl 退款回调通知地址(可选)
|
||||||
|
* @return 退款结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/refund")
|
||||||
|
@Log(title = "微信支付V3退款", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult refund(@RequestParam String orderNo,
|
||||||
|
@RequestParam String refundNo,
|
||||||
|
@RequestParam int totalFee,
|
||||||
|
@RequestParam int refundFee,
|
||||||
|
@RequestParam(required = false) String reason,
|
||||||
|
@RequestParam(required = false) String notifyUrl) {
|
||||||
|
try {
|
||||||
|
log.info("🔄 收到退款请求 - 订单号: {}, 退款单号: {}, 退款金额: {}分", orderNo, refundNo, refundFee);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.refund(orderNo, refundNo, totalFee, refundFee, reason, notifyUrl);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("退款申请成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 退款申请异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("退款申请失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速退款(简化参数)
|
||||||
|
*
|
||||||
|
* @param orderNo 商户订单号
|
||||||
|
* @param refundFee 退款金额(元)
|
||||||
|
* @param reason 退款原因
|
||||||
|
* @return 退款结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/quick-refund")
|
||||||
|
@Log(title = "微信支付V3快速退款", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult quickRefund(@RequestParam String orderNo,
|
||||||
|
@RequestParam BigDecimal refundFee,
|
||||||
|
@RequestParam(required = false) String reason) {
|
||||||
|
try {
|
||||||
|
log.info("⚡ 收到快速退款请求 - 订单号: {}, 退款金额: {}元", orderNo, refundFee);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.quickRefund(orderNo, refundFee, reason);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("快速退款成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 快速退款异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("快速退款失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询退款
|
||||||
|
*
|
||||||
|
* @param refundNo 退款单号
|
||||||
|
* @return 退款查询结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/refund/{refundNo}")
|
||||||
|
@Log(title = "微信支付V3查询退款", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult queryRefund(@PathVariable String refundNo) {
|
||||||
|
try {
|
||||||
|
log.info("🔍 收到退款查询请求 - 退款单号: {}", refundNo);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.queryRefund(refundNo);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("查询退款成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 查询退款异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("查询退款失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户提现
|
||||||
|
*
|
||||||
|
* @param openid 用户openid
|
||||||
|
* @param amount 金额(分)
|
||||||
|
* @param desc 提现描述
|
||||||
|
* @param userName 真实姓名(可选,用于实名校验)
|
||||||
|
* @return 提现结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/withdraw")
|
||||||
|
@Log(title = "微信支付V3用户提现", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult withdraw(@RequestParam String openid,
|
||||||
|
@RequestParam int amount,
|
||||||
|
@RequestParam(required = false) String desc,
|
||||||
|
@RequestParam(required = false) String userName) {
|
||||||
|
try {
|
||||||
|
log.info("💰 收到提现请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("提现申请成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 提现申请异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("提现申请失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速提现(简化参数)
|
||||||
|
*
|
||||||
|
* @param openid 用户openid
|
||||||
|
* @param amount 金额(元)
|
||||||
|
* @param desc 提现描述
|
||||||
|
* @return 提现结果
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/quick-withdraw")
|
||||||
|
@Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult quickWithdraw(@RequestParam String openid,
|
||||||
|
@RequestParam BigDecimal amount,
|
||||||
|
@RequestParam(required = false) String desc) {
|
||||||
|
try {
|
||||||
|
log.info("⚡ 收到快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("快速提现成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 快速提现异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("快速提现失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化提现(不加密敏感字段,用于测试)
|
||||||
|
*
|
||||||
|
* @param openid 用户openid
|
||||||
|
* @param amount 金额(分)
|
||||||
|
* @param desc 提现描述
|
||||||
|
* @return 提现结果
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/withdraw-simple")
|
||||||
|
@Log(title = "微信支付V3简化提现", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult withdrawSimple(@RequestParam String openid,
|
||||||
|
@RequestParam int amount,
|
||||||
|
@RequestParam(required = false) String desc) {
|
||||||
|
try {
|
||||||
|
log.info("🧪 收到简化提现请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.withdrawSimple(openid, amount, desc);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("简化提现申请成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 简化提现申请异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("简化提现申请失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询转账批次
|
||||||
|
*
|
||||||
|
* @param outBatchNo 商户批次单号
|
||||||
|
* @param needDetail 是否需要转账明细(默认false)
|
||||||
|
* @return 转账查询结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/transfer/{outBatchNo}")
|
||||||
|
@Log(title = "微信支付V3查询转账", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult queryTransfer(@PathVariable String outBatchNo,
|
||||||
|
@RequestParam(defaultValue = "false") boolean needDetail) {
|
||||||
|
try {
|
||||||
|
log.info("🔍 收到转账查询请求 - 批次号: {}, 需要明细: {}", outBatchNo, needDetail);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.queryTransfer(outBatchNo, needDetail);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("查询转账成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 查询转账异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("查询转账失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试私钥加载
|
||||||
|
*
|
||||||
|
* @return 测试结果
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@GetMapping("/test-key")
|
||||||
|
@Log(title = "微信支付V3测试私钥", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult testKey() {
|
||||||
|
try {
|
||||||
|
log.info("🔐 开始测试私钥加载");
|
||||||
|
|
||||||
|
// 通过反射调用私有方法测试私钥加载
|
||||||
|
java.lang.reflect.Method method = WechatPayV3Util.class.getDeclaredMethod("getPrivateKey");
|
||||||
|
method.setAccessible(true);
|
||||||
|
Object privateKey = method.invoke(wechatPayV3Util);
|
||||||
|
|
||||||
|
if (privateKey != null) {
|
||||||
|
return AjaxResult.success("私钥加载成功", "私钥算法: " + privateKey.getClass().getSimpleName());
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error("私钥加载失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 测试私钥异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("测试私钥失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额转换工具
|
||||||
|
*
|
||||||
|
* @param yuan 金额(元)
|
||||||
|
* @return 转换结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/convert/yuan-to-fen")
|
||||||
|
public AjaxResult yuanToFen(@RequestParam BigDecimal yuan) {
|
||||||
|
try {
|
||||||
|
int fen = WechatPayV3Util.yuanToFen(yuan);
|
||||||
|
return AjaxResult.success("转换成功", Map.of("yuan", yuan, "fen", fen));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("转换失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金额转换工具
|
||||||
|
*
|
||||||
|
* @param fen 金额(分)
|
||||||
|
* @return 转换结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/convert/fen-to-yuan")
|
||||||
|
public AjaxResult fenToYuan(@RequestParam int fen) {
|
||||||
|
try {
|
||||||
|
BigDecimal yuan = WechatPayV3Util.fenToYuan(fen);
|
||||||
|
return AjaxResult.success("转换成功", Map.of("fen", fen, "yuan", yuan));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("转换失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成订单号
|
||||||
|
*
|
||||||
|
* @param prefix 前缀
|
||||||
|
* @return 订单号
|
||||||
|
*/
|
||||||
|
@GetMapping("/generate-order-no")
|
||||||
|
public AjaxResult generateOrderNo(@RequestParam(defaultValue = "ORDER_") String prefix) {
|
||||||
|
try {
|
||||||
|
String orderNo = WechatPayV3Util.generateOrderNo(prefix);
|
||||||
|
return AjaxResult.success("生成成功", Map.of("orderNo", orderNo));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("生成失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信支付平台证书
|
||||||
|
*
|
||||||
|
* @return 平台证书信息
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@GetMapping("/certificates")
|
||||||
|
@Log(title = "获取微信支付平台证书", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult getCertificates() {
|
||||||
|
try {
|
||||||
|
log.info("📜 收到获取平台证书请求");
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.getCertificates();
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("获取平台证书成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 获取平台证书异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("获取平台证书失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无序列号提现测试
|
||||||
|
*
|
||||||
|
* @param openid 用户openid
|
||||||
|
* @param amount 金额(分)
|
||||||
|
* @param desc 提现描述
|
||||||
|
* @return 提现结果
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/withdraw-without-serial")
|
||||||
|
@Log(title = "微信支付V3无序列号提现测试", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult withdrawWithoutSerial(@RequestParam String openid,
|
||||||
|
@RequestParam int amount,
|
||||||
|
@RequestParam(required = false) String desc) {
|
||||||
|
try {
|
||||||
|
log.info("🧪 收到无序列号提现测试请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount);
|
||||||
|
|
||||||
|
Map<String, Object> result = wechatPayV3Util.withdrawWithoutSerial(openid, amount, desc);
|
||||||
|
|
||||||
|
if ((Boolean) result.get("success")) {
|
||||||
|
return AjaxResult.success("无序列号提现测试成功", result.get("data"));
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error(result.get("message").toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 无序列号提现测试异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("无序列号提现测试失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动更新微信支付平台证书序列号
|
||||||
|
*
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@Anonymous
|
||||||
|
@PostMapping("/auto-update-certificate-serial")
|
||||||
|
@Log(title = "自动更新微信支付平台证书序列号", businessType = BusinessType.OTHER)
|
||||||
|
public AjaxResult autoUpdateCertificateSerial() {
|
||||||
|
try {
|
||||||
|
log.info("🔄 收到自动更新证书序列号请求");
|
||||||
|
|
||||||
|
boolean updated = wechatCertificateService.autoUpdatePlatformCertificateSerial();
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
return AjaxResult.success("微信支付平台证书序列号更新成功,请重启应用使配置生效");
|
||||||
|
} else {
|
||||||
|
return AjaxResult.error("更新失败,请查看日志了解详情");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 自动更新证书序列号异常: {}", e.getMessage(), e);
|
||||||
|
return AjaxResult.error("自动更新失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import com.alibaba.fastjson2.JSONObject;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
import com.ruoyi.system.domain.*;
|
import com.ruoyi.system.domain.*;
|
||||||
|
import com.ruoyi.system.domain.AppleDoMain.OrderApple;
|
||||||
import com.ruoyi.system.service.*;
|
import com.ruoyi.system.service.*;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
|
|
@ -774,6 +775,7 @@ public class AppletControllerUtil {
|
||||||
// 查询该分类下的商品
|
// 查询该分类下的商品
|
||||||
ServiceGoods serviceGoodsQuery = new ServiceGoods();
|
ServiceGoods serviceGoodsQuery = new ServiceGoods();
|
||||||
serviceGoodsQuery.setCateId(category.getId());
|
serviceGoodsQuery.setCateId(category.getId());
|
||||||
|
serviceGoodsQuery.setStatus("1");
|
||||||
|
|
||||||
// 如果有关键词,添加搜索条件
|
// 如果有关键词,添加搜索条件
|
||||||
if (keywords != null && !keywords.trim().isEmpty()) {
|
if (keywords != null && !keywords.trim().isEmpty()) {
|
||||||
|
|
@ -1104,7 +1106,7 @@ public class AppletControllerUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 检查用户状态
|
// 4. 检查用户状态
|
||||||
if (!"1".equals(user.getStatus())) {
|
if (user.getStatus()!=1) {
|
||||||
result.put("valid", false);
|
result.put("valid", false);
|
||||||
result.put("message", "用户账号已被禁用");
|
result.put("message", "用户账号已被禁用");
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -4006,4 +4008,695 @@ public class AppletControllerUtil {
|
||||||
return "未知状态";
|
return "未知状态";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================== 新增的工具方法 ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为分类列表中的图片添加CDN前缀
|
||||||
|
*
|
||||||
|
* @param categoryList 分类列表
|
||||||
|
*/
|
||||||
|
public static void addImageCdnPrefixForCategories(List<ServiceCate> categoryList) {
|
||||||
|
if (categoryList != null) {
|
||||||
|
for (ServiceCate category : categoryList) {
|
||||||
|
category.setIcon(buildImageUrl(category.getIcon()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为广告图片列表添加CDN前缀
|
||||||
|
*
|
||||||
|
* @param advImgList 广告图片列表
|
||||||
|
*/
|
||||||
|
public static void addImageCdnPrefixForAdvImages(List<AdvImg> advImgList) {
|
||||||
|
if (advImgList != null) {
|
||||||
|
for (AdvImg advImg : advImgList) {
|
||||||
|
advImg.setImage(buildImageUrl(advImg.getImage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统配置信息
|
||||||
|
*
|
||||||
|
* @param name 配置名称
|
||||||
|
* @param siteConfigService 配置服务
|
||||||
|
* @return 配置对象,如果不存在返回null
|
||||||
|
*/
|
||||||
|
public static SiteConfig getSiteConfig(String name, ISiteConfigService siteConfigService) {
|
||||||
|
if (name == null || name.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SiteConfig configQuery = new SiteConfig();
|
||||||
|
configQuery.setName(name.trim());
|
||||||
|
List<SiteConfig> configList = siteConfigService.selectSiteConfigList(configQuery);
|
||||||
|
|
||||||
|
return configList.isEmpty() ? null : configList.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析配置值为JSON对象
|
||||||
|
*
|
||||||
|
* @param configValue 配置值字符串
|
||||||
|
* @return JSON对象,解析失败返回空对象
|
||||||
|
*/
|
||||||
|
public static JSONObject parseConfigValue(String configValue) {
|
||||||
|
if (configValue == null || configValue.trim().isEmpty()) {
|
||||||
|
return new JSONObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(configValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new JSONObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证用户地址归属权
|
||||||
|
*
|
||||||
|
* @param addressId 地址ID
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @param userAddressService 地址服务
|
||||||
|
* @return 用户地址对象,如果不存在或无权访问返回null
|
||||||
|
*/
|
||||||
|
public static UserAddress validateUserAddress(Long addressId, Long userId, IUserAddressService userAddressService) {
|
||||||
|
if (addressId == null || userId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserAddress userAddress = userAddressService.selectUserAddressById(addressId);
|
||||||
|
if (userAddress == null || !userAddress.getUid().equals(userId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理地址修改逻辑
|
||||||
|
*
|
||||||
|
* @param params 修改参数
|
||||||
|
* @param user 当前用户
|
||||||
|
* @param userAddressService 地址服务
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static AjaxResult processAddressEdit(Map<String, Object> params, Users user, IUserAddressService userAddressService) {
|
||||||
|
try {
|
||||||
|
// 参数验证
|
||||||
|
if (params == null || params.get("id") == null) {
|
||||||
|
return appletWarning("地址ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
Long addressId;
|
||||||
|
try {
|
||||||
|
addressId = Long.valueOf(params.get("id").toString());
|
||||||
|
if (addressId <= 0) {
|
||||||
|
return appletWarning("地址ID无效");
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return appletWarning("地址ID格式错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询原地址信息并验证归属权
|
||||||
|
UserAddress existingAddress = validateUserAddress(addressId, user.getId(), userAddressService);
|
||||||
|
if (existingAddress == null) {
|
||||||
|
return appletWarning("地址不存在或无权修改");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建更新的地址对象
|
||||||
|
UserAddress updateAddress = buildUpdateAddress(params, addressId, user.getId());
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
String validationResult = validateAddressParams(updateAddress);
|
||||||
|
if (validationResult != null) {
|
||||||
|
return appletWarning(validationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理默认地址逻辑
|
||||||
|
if (updateAddress.getIsDefault() != null && updateAddress.getIsDefault() == 1L) {
|
||||||
|
userAddressService.updateUserAddressDefault(user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行地址更新
|
||||||
|
int updateResult = userAddressService.updateUserAddress(updateAddress);
|
||||||
|
|
||||||
|
if (updateResult > 0) {
|
||||||
|
return appletSuccess("地址修改成功");
|
||||||
|
} else {
|
||||||
|
return appletWarning("地址修改失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return appletError("修改地址失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理地址新增逻辑
|
||||||
|
*
|
||||||
|
* @param params 新增参数
|
||||||
|
* @param user 当前用户
|
||||||
|
* @param userAddressService 地址服务
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static AjaxResult processAddressAdd(Map<String, Object> params, Users user, IUserAddressService userAddressService) {
|
||||||
|
try {
|
||||||
|
// 构建新增的地址对象
|
||||||
|
UserAddress newAddress = buildNewAddress(params, user.getId());
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
String validationResult = validateAddressParams(newAddress);
|
||||||
|
if (validationResult != null) {
|
||||||
|
return appletWarning(validationResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理默认地址逻辑
|
||||||
|
if (newAddress.getIsDefault() != null && newAddress.getIsDefault() == 1L) {
|
||||||
|
userAddressService.updateUserAddressDefault(user.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行地址新增
|
||||||
|
int insertResult = userAddressService.insertUserAddress(newAddress);
|
||||||
|
|
||||||
|
if (insertResult > 0) {
|
||||||
|
return appletSuccess("地址新增成功");
|
||||||
|
} else {
|
||||||
|
return appletWarning("地址新增失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return appletError("新增地址失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理售后返修申请
|
||||||
|
*
|
||||||
|
* @param params 申请参数
|
||||||
|
* @param user 当前用户
|
||||||
|
* @param orderService 订单服务
|
||||||
|
* @param orderReworkService 返修服务
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static AjaxResult processReworkApplication(Map<String, Object> params, Users user,
|
||||||
|
IOrderService orderService, IOrderReworkService orderReworkService) {
|
||||||
|
try {
|
||||||
|
// 参数验证
|
||||||
|
if (params == null) {
|
||||||
|
return appletWarning("请求参数不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String orderId = (String) params.get("order_id");
|
||||||
|
String phone = (String) params.get("phone");
|
||||||
|
String mark = (String) params.get("mark");
|
||||||
|
|
||||||
|
if (orderId == null || orderId.isEmpty()) {
|
||||||
|
return appletWarning("订单ID不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phone == null || phone.isEmpty()) {
|
||||||
|
return appletWarning("联系电话不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证手机号格式
|
||||||
|
if (!phone.matches("^1[3-9]\\d{9}$")) {
|
||||||
|
return appletWarning("联系电话格式不正确");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mark == null || mark.isEmpty()) {
|
||||||
|
return appletWarning("返修原因不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询订单信息并验证归属权
|
||||||
|
Order order = orderService.selectOrderByOrderId(orderId);
|
||||||
|
if (order == null) {
|
||||||
|
return appletWarning("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!order.getUid().equals(user.getId())) {
|
||||||
|
return appletWarning("无权操作此订单");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证订单状态是否允许申请售后
|
||||||
|
if (!isOrderAllowRework(order.getStatus())) {
|
||||||
|
return appletWarning("当前订单状态不允许申请售后");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理售后返修申请
|
||||||
|
boolean reworkResult = processReworkApplication(order, phone, mark, user, orderReworkService, orderService);
|
||||||
|
|
||||||
|
if (reworkResult) {
|
||||||
|
return appletSuccess("售后返修申请已提交,我们会尽快联系您处理");
|
||||||
|
} else {
|
||||||
|
return appletWarning("售后申请提交失败,请稍后重试");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return appletError("售后申请失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序查询用户余额日志接口(简化版)
|
||||||
|
*
|
||||||
|
* @param params 请求参数Map,包含分页参数page、limit
|
||||||
|
* @param token 用户token
|
||||||
|
* @param usersService 用户服务
|
||||||
|
* @param userMemnerConsumptionLogService 用户余额日志服务
|
||||||
|
* @return 余额日志分页数据
|
||||||
|
*
|
||||||
|
* 请求参数:
|
||||||
|
* - page: 页码,默认1
|
||||||
|
* - limit: 每页数量,默认10,最大50
|
||||||
|
*
|
||||||
|
* 返回数据格式:
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "msg": "OK",
|
||||||
|
* "data": {
|
||||||
|
* "current_page": 1,
|
||||||
|
* "data": [
|
||||||
|
* {
|
||||||
|
* "id": 1,
|
||||||
|
* "amount": "+100.00",
|
||||||
|
* "before_amount": "0.00",
|
||||||
|
* "after_amount": "100.00",
|
||||||
|
* "remark": "服务收入",
|
||||||
|
* "created_time": "2024-01-01 12:00:00"
|
||||||
|
* }
|
||||||
|
* ],
|
||||||
|
* "total": 100,
|
||||||
|
* "per_page": 10,
|
||||||
|
* "last_page": 10,
|
||||||
|
* "has_more": true
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public static AjaxResult getUserBalanceLogList(Map<String, Object> params, String token,
|
||||||
|
IUsersService usersService,
|
||||||
|
IUserMemnerConsumptionLogService userMemnerConsumptionLogService) {
|
||||||
|
try {
|
||||||
|
// 1. 验证用户token
|
||||||
|
Map<String, Object> tokenValidation = validateUserToken(token, usersService);
|
||||||
|
if (!(Boolean) tokenValidation.get("valid")) {
|
||||||
|
return appletUnauthorized((String) tokenValidation.get("message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从tokenValidation中获取用户信息Map,然后提取userId
|
||||||
|
Map<String, Object> userInfoMap = (Map<String, Object>) tokenValidation.get("userInfo");
|
||||||
|
Long userId = (Long) userInfoMap.get("userId");
|
||||||
|
|
||||||
|
// 2. 解析分页参数
|
||||||
|
int page = 1;
|
||||||
|
int limit = 10;
|
||||||
|
|
||||||
|
if (params != null && params.get("page") != null) {
|
||||||
|
try {
|
||||||
|
page = Integer.parseInt(params.get("page").toString());
|
||||||
|
if (page < 1) page = 1;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return appletWarning("页码参数格式错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params != null && params.get("limit") != null) {
|
||||||
|
try {
|
||||||
|
limit = Integer.parseInt(params.get("limit").toString());
|
||||||
|
if (limit < 1) limit = 10;
|
||||||
|
if (limit > 50) limit = 50; // 限制最大每页数量
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return appletWarning("每页数量参数格式错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 设置分页
|
||||||
|
PageHelper.startPage(page, limit);
|
||||||
|
|
||||||
|
// 4. 构建查询条件
|
||||||
|
UserMemnerConsumptionLog queryCondition = new UserMemnerConsumptionLog();
|
||||||
|
queryCondition.setUid(userId.intValue());
|
||||||
|
|
||||||
|
// 5. 查询余额日志列表
|
||||||
|
List<UserMemnerConsumptionLog> logList = userMemnerConsumptionLogService.selectUserMemnerConsumptionLogList(queryCondition);
|
||||||
|
|
||||||
|
// 6. 构建返回数据
|
||||||
|
List<Map<String, Object>> formattedLogList = buildSimpleBalanceLogResponseList(logList);
|
||||||
|
|
||||||
|
// 7. 构建分页信息
|
||||||
|
PageInfo<UserMemnerConsumptionLog> pageInfo = new PageInfo<>(logList);
|
||||||
|
Map<String, Object> responseData = new HashMap<>();
|
||||||
|
responseData.put("current_page", pageInfo.getPageNum());
|
||||||
|
responseData.put("data", formattedLogList);
|
||||||
|
responseData.put("total", pageInfo.getTotal());
|
||||||
|
responseData.put("per_page", pageInfo.getPageSize());
|
||||||
|
responseData.put("last_page", pageInfo.getPages());
|
||||||
|
responseData.put("has_more", pageInfo.isHasNextPage());
|
||||||
|
|
||||||
|
return appletSuccess(responseData);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("查询用户余额日志异常:" + e.getMessage());
|
||||||
|
return appletError("查询余额日志失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建简化的用户余额日志响应列表
|
||||||
|
*
|
||||||
|
* @param logList 原始日志列表
|
||||||
|
* @return 格式化的日志响应列表
|
||||||
|
*/
|
||||||
|
public static List<Map<String, Object>> buildSimpleBalanceLogResponseList(List<UserMemnerConsumptionLog> logList) {
|
||||||
|
List<Map<String, Object>> formattedList = new ArrayList<>();
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
for (UserMemnerConsumptionLog log : logList) {
|
||||||
|
Map<String, Object> logData = new HashMap<>();
|
||||||
|
|
||||||
|
// 基础信息
|
||||||
|
logData.put("id", log.getId());
|
||||||
|
|
||||||
|
// 金额信息格式化
|
||||||
|
BigDecimal amount = log.getConsumptionmoney() != null ? log.getConsumptionmoney() : BigDecimal.ZERO;
|
||||||
|
String amountStr;
|
||||||
|
if (log.getType() != null && log.getType() == 1) {
|
||||||
|
// 收入显示正号
|
||||||
|
amountStr = "+" + amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
|
||||||
|
} else {
|
||||||
|
// 支出显示负号
|
||||||
|
amountStr = "-" + amount.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
|
||||||
|
}
|
||||||
|
logData.put("amount", amountStr);
|
||||||
|
|
||||||
|
// 余额信息
|
||||||
|
logData.put("before_amount", log.getBeformoney() != null ?
|
||||||
|
log.getBeformoney().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : "0.00");
|
||||||
|
logData.put("after_amount", log.getAftermoney() != null ?
|
||||||
|
log.getAftermoney().setScale(2, BigDecimal.ROUND_HALF_UP).toString() : "0.00");
|
||||||
|
|
||||||
|
// 备注信息
|
||||||
|
logData.put("remark", log.getReamk() != null ? log.getReamk() : "");
|
||||||
|
|
||||||
|
// 时间信息
|
||||||
|
if (log.getCreatedAt() != null) {
|
||||||
|
logData.put("created_time", sdf.format(log.getCreatedAt()));
|
||||||
|
} else if (log.getConsumptiontime() != null) {
|
||||||
|
logData.put("created_time", sdf.format(log.getConsumptiontime()));
|
||||||
|
} else {
|
||||||
|
logData.put("created_time", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedList.add(logData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================== 用户余额日志接口响应类 ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户余额日志接口统一响应类
|
||||||
|
* 适用于 /api/user/benefit/log 接口
|
||||||
|
*/
|
||||||
|
public static class UserBalanceLogResponse {
|
||||||
|
/**
|
||||||
|
* 响应状态码
|
||||||
|
* 200: 成功
|
||||||
|
* 422: 业务提示
|
||||||
|
* 332: 未登录
|
||||||
|
* 500: 系统错误
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应消息
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应数据
|
||||||
|
*/
|
||||||
|
private UserBalanceLogPageData data;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public UserBalanceLogResponse() {}
|
||||||
|
|
||||||
|
public UserBalanceLogResponse(Integer code, String msg, UserBalanceLogPageData data) {
|
||||||
|
this.code = code;
|
||||||
|
this.msg = msg;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter 和 Setter
|
||||||
|
public Integer getCode() { return code; }
|
||||||
|
public void setCode(Integer code) { this.code = code; }
|
||||||
|
|
||||||
|
public String getMsg() { return msg; }
|
||||||
|
public void setMsg(String msg) { this.msg = msg; }
|
||||||
|
|
||||||
|
public UserBalanceLogPageData getData() { return data; }
|
||||||
|
public void setData(UserBalanceLogPageData data) { this.data = data; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户余额日志分页数据类
|
||||||
|
*/
|
||||||
|
public static class UserBalanceLogPageData {
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
private Integer current_page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页数量
|
||||||
|
*/
|
||||||
|
private Integer per_page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总记录数
|
||||||
|
*/
|
||||||
|
private Long total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
private Integer last_page;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否还有更多数据
|
||||||
|
*/
|
||||||
|
private Boolean has_more;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 余额日志列表
|
||||||
|
*/
|
||||||
|
private List<UserBalanceLogItem> data;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public UserBalanceLogPageData() {}
|
||||||
|
|
||||||
|
// Getter 和 Setter
|
||||||
|
public Integer getCurrent_page() { return current_page; }
|
||||||
|
public void setCurrent_page(Integer current_page) { this.current_page = current_page; }
|
||||||
|
|
||||||
|
public Integer getPer_page() { return per_page; }
|
||||||
|
public void setPer_page(Integer per_page) { this.per_page = per_page; }
|
||||||
|
|
||||||
|
public Long getTotal() { return total; }
|
||||||
|
public void setTotal(Long total) { this.total = total; }
|
||||||
|
|
||||||
|
public Integer getLast_page() { return last_page; }
|
||||||
|
public void setLast_page(Integer last_page) { this.last_page = last_page; }
|
||||||
|
|
||||||
|
public Boolean getHas_more() { return has_more; }
|
||||||
|
public void setHas_more(Boolean has_more) { this.has_more = has_more; }
|
||||||
|
|
||||||
|
public List<UserBalanceLogItem> getData() { return data; }
|
||||||
|
public void setData(List<UserBalanceLogItem> data) { this.data = data; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户余额日志单条记录类
|
||||||
|
*/
|
||||||
|
public static class UserBalanceLogItem {
|
||||||
|
/**
|
||||||
|
* 日志记录ID
|
||||||
|
*/
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动金额
|
||||||
|
* 格式:"+100.00" 表示收入,"-50.00" 表示支出
|
||||||
|
*/
|
||||||
|
private String amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动前余额
|
||||||
|
* 格式:保留两位小数,如 "150.00"
|
||||||
|
*/
|
||||||
|
private String before_amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动后余额
|
||||||
|
* 格式:保留两位小数,如 "250.00"
|
||||||
|
*/
|
||||||
|
private String after_amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变动说明/备注
|
||||||
|
* 如:服务收入、商品购买、提现等
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录创建时间
|
||||||
|
* 格式:yyyy-MM-dd HH:mm:ss
|
||||||
|
*/
|
||||||
|
private String created_time;
|
||||||
|
|
||||||
|
// 构造方法
|
||||||
|
public UserBalanceLogItem() {}
|
||||||
|
|
||||||
|
public UserBalanceLogItem(Integer id, String amount, String before_amount,
|
||||||
|
String after_amount, String remark, String created_time) {
|
||||||
|
this.id = id;
|
||||||
|
this.amount = amount;
|
||||||
|
this.before_amount = before_amount;
|
||||||
|
this.after_amount = after_amount;
|
||||||
|
this.remark = remark;
|
||||||
|
this.created_time = created_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter 和 Setter
|
||||||
|
public Integer getId() { return id; }
|
||||||
|
public void setId(Integer id) { this.id = id; }
|
||||||
|
|
||||||
|
public String getAmount() { return amount; }
|
||||||
|
public void setAmount(String amount) { this.amount = amount; }
|
||||||
|
|
||||||
|
public String getBefore_amount() { return before_amount; }
|
||||||
|
public void setBefore_amount(String before_amount) { this.before_amount = before_amount; }
|
||||||
|
|
||||||
|
public String getAfter_amount() { return after_amount; }
|
||||||
|
public void setAfter_amount(String after_amount) { this.after_amount = after_amount; }
|
||||||
|
|
||||||
|
public String getRemark() { return remark; }
|
||||||
|
public void setRemark(String remark) { this.remark = remark; }
|
||||||
|
|
||||||
|
public String getCreated_time() { return created_time; }
|
||||||
|
public void setCreated_time(String created_time) { this.created_time = created_time; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用户余额日志响应对象的工具方法
|
||||||
|
*
|
||||||
|
* @param code 响应码
|
||||||
|
* @param msg 响应消息
|
||||||
|
* @param pageInfo 分页信息
|
||||||
|
* @param logList 日志列表
|
||||||
|
* @return 完整的响应对象
|
||||||
|
*/
|
||||||
|
public static UserBalanceLogResponse buildUserBalanceLogResponse(Integer code, String msg,
|
||||||
|
PageInfo<?> pageInfo,
|
||||||
|
List<Map<String, Object>> logList) {
|
||||||
|
UserBalanceLogResponse response = new UserBalanceLogResponse();
|
||||||
|
response.setCode(code);
|
||||||
|
response.setMsg(msg);
|
||||||
|
|
||||||
|
if (pageInfo != null && logList != null) {
|
||||||
|
UserBalanceLogPageData pageData = new UserBalanceLogPageData();
|
||||||
|
pageData.setCurrent_page(pageInfo.getPageNum());
|
||||||
|
pageData.setPer_page(pageInfo.getPageSize());
|
||||||
|
pageData.setTotal(pageInfo.getTotal());
|
||||||
|
pageData.setLast_page(pageInfo.getPages());
|
||||||
|
pageData.setHas_more(pageInfo.isHasNextPage());
|
||||||
|
|
||||||
|
// 转换日志列表
|
||||||
|
List<UserBalanceLogItem> items = new ArrayList<>();
|
||||||
|
for (Map<String, Object> logMap : logList) {
|
||||||
|
UserBalanceLogItem item = new UserBalanceLogItem();
|
||||||
|
item.setId((Integer) logMap.get("id"));
|
||||||
|
item.setAmount((String) logMap.get("amount"));
|
||||||
|
item.setBefore_amount((String) logMap.get("before_amount"));
|
||||||
|
item.setAfter_amount((String) logMap.get("after_amount"));
|
||||||
|
item.setRemark((String) logMap.get("remark"));
|
||||||
|
item.setCreated_time((String) logMap.get("created_time"));
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
pageData.setData(items);
|
||||||
|
response.setData(pageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户余额日志接口实际返回值备注说明
|
||||||
|
*
|
||||||
|
* API接口:/api/user/benefit/log
|
||||||
|
*
|
||||||
|
* 完整返回值结构说明:
|
||||||
|
* {
|
||||||
|
* "msg": "OK", // 响应消息,成功时为"OK"
|
||||||
|
* "code": 200, // 响应状态码:200=成功,422=业务提示,332=未登录,500=系统错误
|
||||||
|
* "data": { // 响应数据
|
||||||
|
* "per_page": "10", // 每页显示条数,字符串类型
|
||||||
|
* "total": 1, // 总记录数,整数类型
|
||||||
|
* "data": [ // 日志记录列表
|
||||||
|
* {
|
||||||
|
* "createBy": null, // 创建人,通常为null
|
||||||
|
* "createTime": null, // 创建时间,通常为null
|
||||||
|
* "updateBy": null, // 更新人,通常为null
|
||||||
|
* "updateTime": null, // 更新时间,通常为null
|
||||||
|
* "remark": null, // 系统备注,通常为null
|
||||||
|
* "id": 1, // 记录ID,整数类型,唯一标识
|
||||||
|
* "type": 1, // 变动类型:1=收入,2=支出
|
||||||
|
* "dotime": "2025-07-04", // 操作日期,格式:yyyy-MM-dd
|
||||||
|
* "ordermoney": 10.00, // 订单金额,BigDecimal类型,保留2位小数
|
||||||
|
* "money": 1.00, // 变动金额,BigDecimal类型,保留2位小数
|
||||||
|
* "ordertype": 1, // 订单类型:1=服务订单,2=商品订单,3=其他
|
||||||
|
* "uid": 2750, // 用户ID,整数类型
|
||||||
|
* "beformoney": 0.00, // 变动前金额,BigDecimal类型,保留2位小数
|
||||||
|
* "aftremoney": 10.00, // 变动后金额,BigDecimal类型,保留2位小数
|
||||||
|
* "createdAt": "2025-07-04", // 创建日期,格式:yyyy-MM-dd
|
||||||
|
* "updatedAt": null, // 更新日期,通常为null
|
||||||
|
* "orderid": 4, // 关联订单ID,整数类型
|
||||||
|
* "reamk": "5" // 用户备注/说明,字符串类型
|
||||||
|
* }
|
||||||
|
* ],
|
||||||
|
* "last_page": 1, // 最后一页页码,整数类型
|
||||||
|
* "next_page_url": null, // 下一页URL,通常为null
|
||||||
|
* "from": 1, // 当前页起始记录位置
|
||||||
|
* "to": 1, // 当前页结束记录位置
|
||||||
|
* "prev_page_url": null, // 上一页URL,通常为null
|
||||||
|
* "current_page": 1 // 当前页码,整数类型
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 字段类型说明:
|
||||||
|
* - BigDecimal字段:ordermoney, money, beformoney, aftremoney
|
||||||
|
* - Integer字段:id, type, ordertype, uid, orderid, total, last_page, from, to, current_page
|
||||||
|
* - String字段:dotime, createdAt, updatedAt, reamk, per_page
|
||||||
|
* - 可为null的字段:createBy, createTime, updateBy, updateTime, remark, updatedAt, next_page_url, prev_page_url
|
||||||
|
*
|
||||||
|
* 业务逻辑说明:
|
||||||
|
* 1. type=1时表示收入,money为正值;type=2时表示支出,money为负值
|
||||||
|
* 2. beformoney + money = aftremoney(变动前金额 + 变动金额 = 变动后金额)
|
||||||
|
* 3. orderid关联具体的订单记录
|
||||||
|
* 4. reamk字段存储用户的备注信息
|
||||||
|
* 5. ordermoney是关联订单的总金额
|
||||||
|
* 6. ordertype区分不同类型的订单来源
|
||||||
|
*
|
||||||
|
* 前端展示建议:
|
||||||
|
* 1. 金额显示:根据type字段决定显示正负号,如"+1.00"或"-1.00"
|
||||||
|
* 2. 时间显示:优先使用createdAt,格式化为"yyyy-MM-dd HH:mm:ss"
|
||||||
|
* 3. 备注显示:使用reamk字段内容
|
||||||
|
* 4. 分页信息:使用current_page、total、last_page构建分页组件
|
||||||
|
*
|
||||||
|
* 错误处理:
|
||||||
|
* - code!=200时,显示msg中的错误信息
|
||||||
|
* - data字段可能为null或空数组
|
||||||
|
* - 金额字段可能为null,需要默认值处理
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
package com.ruoyi.system.ControllerUtil;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
import com.ruoyi.system.config.WechatConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpEntity;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付平台证书获取工具类
|
||||||
|
*
|
||||||
|
* @author Mr. Zhang Pan
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-01-17
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class WechatCertificateUtil {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WechatCertificateUtil.class);
|
||||||
|
private static final String V3_BASE_URL = "https://api.mch.weixin.qq.com";
|
||||||
|
private static final String CERTIFICATES_URL = "/v3/certificates";
|
||||||
|
private static final String ALGORITHM_RSA_SHA256 = "SHA256withRSA";
|
||||||
|
private static final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
private static WechatConfig wechatConfig() {
|
||||||
|
return SpringUtils.getBean(WechatConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信支付平台证书序列号
|
||||||
|
*
|
||||||
|
* @return 平台证书序列号
|
||||||
|
*/
|
||||||
|
public String getPlatformCertificateSerial() {
|
||||||
|
log.info("🔍 开始获取微信支付平台证书序列号");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 构建请求
|
||||||
|
String fullUrl = V3_BASE_URL + CERTIFICATES_URL;
|
||||||
|
HttpHeaders headers = buildHeaders();
|
||||||
|
|
||||||
|
log.info("🚀 发送请求到: {}", fullUrl);
|
||||||
|
|
||||||
|
HttpEntity<String> requestEntity = new HttpEntity<>("", headers);
|
||||||
|
ResponseEntity<String> response = restTemplate.exchange(
|
||||||
|
fullUrl,
|
||||||
|
HttpMethod.GET,
|
||||||
|
requestEntity,
|
||||||
|
String.class
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.getStatusCode().is2xxSuccessful()) {
|
||||||
|
String responseBody = response.getBody();
|
||||||
|
JSONObject jsonResponse = JSONObject.parseObject(responseBody);
|
||||||
|
|
||||||
|
if (jsonResponse != null && jsonResponse.containsKey("data")) {
|
||||||
|
JSONArray dataArray = jsonResponse.getJSONArray("data");
|
||||||
|
if (dataArray != null && !dataArray.isEmpty()) {
|
||||||
|
JSONObject firstCert = dataArray.getJSONObject(0);
|
||||||
|
String serialNo = firstCert.getString("serial_no");
|
||||||
|
|
||||||
|
log.info("✅ 成功获取平台证书序列号: {}", maskSensitiveData(serialNo));
|
||||||
|
return serialNo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.error("❌ 获取平台证书序列号失败,响应: {}", response.getBody());
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 获取平台证书序列号异常: {}", e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建请求头
|
||||||
|
*/
|
||||||
|
private HttpHeaders buildHeaders() {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.set("Content-Type", "application/json");
|
||||||
|
headers.set("Accept", "application/json");
|
||||||
|
headers.set("User-Agent", "RuoYi-WechatPay-V3");
|
||||||
|
|
||||||
|
// 构建Authorization头
|
||||||
|
String authorization = buildAuthorizationHeader();
|
||||||
|
headers.set("Authorization", authorization);
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建Authorization头
|
||||||
|
*/
|
||||||
|
private String buildAuthorizationHeader() {
|
||||||
|
long timestamp = System.currentTimeMillis() / 1000;
|
||||||
|
String nonceStr = generateNonceStr();
|
||||||
|
String signature = buildSignature("GET", CERTIFICATES_URL, timestamp, nonceStr, "");
|
||||||
|
|
||||||
|
return String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\",timestamp=\"%d\",serial_no=\"%s\"",
|
||||||
|
wechatConfig().getMchid(), nonceStr, signature, timestamp, wechatConfig().getSerialNo());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成V3签名
|
||||||
|
*/
|
||||||
|
private String buildSignature(String method, String url, long timestamp, String nonceStr, String body) {
|
||||||
|
String message = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
PrivateKey privateKey = getPrivateKey();
|
||||||
|
Signature signature = Signature.getInstance(ALGORITHM_RSA_SHA256);
|
||||||
|
signature.initSign(privateKey);
|
||||||
|
signature.update(message.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return Base64.getEncoder().encodeToString(signature.sign());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 生成签名失败: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("生成签名失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取商户私钥
|
||||||
|
*/
|
||||||
|
private PrivateKey getPrivateKey() {
|
||||||
|
try {
|
||||||
|
String privateKeyPath = wechatConfig().getPrivateKeyPath();
|
||||||
|
String privateKeyContent;
|
||||||
|
|
||||||
|
if (privateKeyPath.startsWith("classpath:")) {
|
||||||
|
String resourcePath = privateKeyPath.substring(10);
|
||||||
|
try (var inputStream = this.getClass().getClassLoader().getResourceAsStream(resourcePath)) {
|
||||||
|
if (inputStream == null) {
|
||||||
|
throw new RuntimeException("无法找到classpath资源: " + resourcePath);
|
||||||
|
}
|
||||||
|
privateKeyContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try (var inputStream = this.getClass().getClassLoader().getResourceAsStream(privateKeyPath)) {
|
||||||
|
if (inputStream == null) {
|
||||||
|
throw new RuntimeException("无法找到classpath资源: " + privateKeyPath);
|
||||||
|
}
|
||||||
|
privateKeyContent = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理私钥格式
|
||||||
|
privateKeyContent = privateKeyContent
|
||||||
|
.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||||
|
.replace("-----END PRIVATE KEY-----", "")
|
||||||
|
.replaceAll("\\s+", "");
|
||||||
|
|
||||||
|
byte[] keyBytes = Base64.getDecoder().decode(privateKeyContent);
|
||||||
|
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
return keyFactory.generatePrivate(spec);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 获取商户私钥失败: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("获取商户私钥失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机字符串
|
||||||
|
*/
|
||||||
|
private String generateNonceStr() {
|
||||||
|
return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏显示敏感数据
|
||||||
|
*/
|
||||||
|
private String maskSensitiveData(String data) {
|
||||||
|
if (data == null || data.length() <= 6) {
|
||||||
|
return "****";
|
||||||
|
}
|
||||||
|
return data.substring(0, 3) + "****" + data.substring(data.length() - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,167 @@
|
||||||
|
package com.ruoyi.system.service;
|
||||||
|
|
||||||
|
import com.ruoyi.system.ControllerUtil.WechatCertificateUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付证书管理服务
|
||||||
|
*
|
||||||
|
* @author Mr. Zhang Pan
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-01-17
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class WechatCertificateService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(WechatCertificateService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WechatCertificateUtil wechatCertificateUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动获取并更新微信支付平台证书序列号到配置文件
|
||||||
|
*
|
||||||
|
* @return 是否更新成功
|
||||||
|
*/
|
||||||
|
public boolean autoUpdatePlatformCertificateSerial() {
|
||||||
|
log.info("🔄 开始自动更新微信支付平台证书序列号");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取平台证书序列号
|
||||||
|
String platformSerial = wechatCertificateUtil.getPlatformCertificateSerial();
|
||||||
|
|
||||||
|
if (platformSerial == null || platformSerial.trim().isEmpty()) {
|
||||||
|
log.error("❌ 无法获取微信支付平台证书序列号");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新配置文件
|
||||||
|
boolean updated = updateConfigFile(platformSerial);
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
log.info("✅ 微信支付平台证书序列号更新成功: {}", maskSensitiveData(platformSerial));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log.error("❌ 更新配置文件失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ 自动更新微信支付平台证书序列号异常: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新配置文件中的微信支付平台证书序列号
|
||||||
|
*
|
||||||
|
* @param platformSerial 平台证书序列号
|
||||||
|
* @return 是否更新成功
|
||||||
|
*/
|
||||||
|
private boolean updateConfigFile(String platformSerial) {
|
||||||
|
try {
|
||||||
|
// 配置文件路径
|
||||||
|
Path configPath = Paths.get("ruoyi-admin/src/main/resources/application.yml");
|
||||||
|
|
||||||
|
if (!Files.exists(configPath)) {
|
||||||
|
log.error("❌ 配置文件不存在: {}", configPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
List<String> lines = Files.readAllLines(configPath);
|
||||||
|
boolean updated = false;
|
||||||
|
|
||||||
|
// 查找并更新配置
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
String line = lines.get(i);
|
||||||
|
|
||||||
|
// 查找注释掉的wechatpay-serial配置
|
||||||
|
if (line.trim().startsWith("# wechatpay-serial:")) {
|
||||||
|
// 替换为有效配置
|
||||||
|
String indent = getIndentation(line);
|
||||||
|
lines.set(i, indent + "wechatpay-serial: " + platformSerial);
|
||||||
|
updated = true;
|
||||||
|
log.info("✅ 更新配置行: {}", lines.get(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找已存在的wechatpay-serial配置
|
||||||
|
if (line.trim().startsWith("wechatpay-serial:")) {
|
||||||
|
// 更新现有配置
|
||||||
|
String indent = getIndentation(line);
|
||||||
|
lines.set(i, indent + "wechatpay-serial: " + platformSerial);
|
||||||
|
updated = true;
|
||||||
|
log.info("✅ 更新配置行: {}", lines.get(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到配置,则添加新配置
|
||||||
|
if (!updated) {
|
||||||
|
// 查找wechat配置块,在其中添加新配置
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
String line = lines.get(i);
|
||||||
|
if (line.trim().startsWith("cert-dir:")) {
|
||||||
|
// 在cert-dir后面添加新配置
|
||||||
|
String indent = getIndentation(line);
|
||||||
|
lines.add(i + 1, indent + "# 微信支付平台证书序列号(自动获取)");
|
||||||
|
lines.add(i + 2, indent + "wechatpay-serial: " + platformSerial);
|
||||||
|
updated = true;
|
||||||
|
log.info("✅ 添加新配置: wechatpay-serial: {}", platformSerial);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
// 写回文件
|
||||||
|
Files.write(configPath, lines, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
|
log.info("✅ 配置文件更新成功: {}", configPath);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ 未找到合适的位置更新配置");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("❌ 更新配置文件异常: {}", e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取行的缩进
|
||||||
|
*/
|
||||||
|
private String getIndentation(String line) {
|
||||||
|
int indent = 0;
|
||||||
|
for (char c : line.toCharArray()) {
|
||||||
|
if (c == ' ') {
|
||||||
|
indent++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return " ".repeat(indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏显示敏感数据
|
||||||
|
*/
|
||||||
|
private String maskSensitiveData(String data) {
|
||||||
|
if (data == null || data.length() <= 6) {
|
||||||
|
return "****";
|
||||||
|
}
|
||||||
|
return data.substring(0, 3) + "****" + data.substring(data.length() - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -49,8 +49,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">status,</if>
|
<if test="status != null">status,</if>
|
||||||
<if test="workerimage != null">workerimage,</if>
|
<if test="workerimage != null">workerimage,</if>
|
||||||
<if test="quotationTime != null">quotation_time,</if>
|
<if test="quotationTime != null">quotation_time,</if>
|
||||||
<if test="createdAt != null">created_at,</if>
|
created_at,
|
||||||
<if test="updatedAt != null">updated_at,</if>
|
updated_at
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="id != null">#{id},</if>
|
<if test="id != null">#{id},</if>
|
||||||
|
|
@ -61,8 +61,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">#{status},</if>
|
<if test="status != null">#{status},</if>
|
||||||
<if test="workerimage != null">#{workerimage},</if>
|
<if test="workerimage != null">#{workerimage},</if>
|
||||||
<if test="quotationTime != null">#{quotationTime},</if>
|
<if test="quotationTime != null">#{quotationTime},</if>
|
||||||
<if test="createdAt != null">#{createdAt},</if>
|
NOW(),
|
||||||
<if test="updatedAt != null">#{updatedAt},</if>
|
NOW()
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -76,8 +76,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="status != null">status = #{status},</if>
|
<if test="status != null">status = #{status},</if>
|
||||||
<if test="workerimage != null">workerimage = #{workerimage},</if>
|
<if test="workerimage != null">workerimage = #{workerimage},</if>
|
||||||
<if test="quotationTime != null">quotation_time = #{quotationTime},</if>
|
<if test="quotationTime != null">quotation_time = #{quotationTime},</if>
|
||||||
<if test="createdAt != null">created_at = #{createdAt},</if>
|
updated_at = NOW(),
|
||||||
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
|
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="transactionId != null">transaction_id,</if>
|
<if test="transactionId != null">transaction_id,</if>
|
||||||
<if test="paytype != null">paytype,</if>
|
<if test="paytype != null">paytype,</if>
|
||||||
<if test="productId != null">product_id,</if>
|
<if test="productId != null">product_id,</if>
|
||||||
<if test="createdAt != null">created_at,</if>
|
created_at,
|
||||||
<if test="updatedAt != null">updated_at,</if>
|
updated_at
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="id != null">#{id},</if>
|
<if test="id != null">#{id},</if>
|
||||||
|
|
@ -68,8 +68,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="transactionId != null">#{transactionId},</if>
|
<if test="transactionId != null">#{transactionId},</if>
|
||||||
<if test="paytype != null">#{paytype},</if>
|
<if test="paytype != null">#{paytype},</if>
|
||||||
<if test="productId != null">#{productId},</if>
|
<if test="productId != null">#{productId},</if>
|
||||||
<if test="createdAt != null">#{createdAt},</if>
|
NOW(),
|
||||||
<if test="updatedAt != null">#{updatedAt},</if>
|
NOW()
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
|
@ -86,8 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="transactionId != null">transaction_id = #{transactionId},</if>
|
<if test="transactionId != null">transaction_id = #{transactionId},</if>
|
||||||
<if test="paytype != null">paytype = #{paytype},</if>
|
<if test="paytype != null">paytype = #{paytype},</if>
|
||||||
<if test="productId != null">product_id = #{productId},</if>
|
<if test="productId != null">product_id = #{productId},</if>
|
||||||
<if test="createdAt != null">created_at = #{createdAt},</if>
|
updated_at = NOW(),
|
||||||
<if test="updatedAt != null">updated_at = #{updatedAt},</if>
|
|
||||||
</trim>
|
</trim>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,12 @@
|
||||||
<include refid="selectUsersInvoiceInfoVo"/>
|
<include refid="selectUsersInvoiceInfoVo"/>
|
||||||
<where>
|
<where>
|
||||||
<if test="uid != null "> and uid = #{uid}</if>
|
<if test="uid != null "> and uid = #{uid}</if>
|
||||||
<if test="invoiceTitle != null and invoiceTitle != ''"> and invoice_title = #{invoiceTitle}</if>
|
<if test="invoiceTitle != null and invoiceTitle != ''"> and invoice_title like concat('%', #{invoiceTitle}, '%')</if>
|
||||||
<if test="taxNumber != null and taxNumber != ''"> and tax_number = #{taxNumber}</if>
|
<if test="taxNumber != null and taxNumber != ''"> and tax_number = #{taxNumber}</if>
|
||||||
<if test="bankName != null and bankName != ''"> and bank_name like concat('%', #{bankName}, '%')</if>
|
<if test="bankName != null and bankName != ''"> and bank_name like concat('%', #{bankName}, '%')</if>
|
||||||
<if test="bankAccount != null and bankAccount != ''"> and bank_account = #{bankAccount}</if>
|
<if test="bankAccount != null and bankAccount != ''"> and bank_account = #{bankAccount}</if>
|
||||||
<if test="address != null and address != ''"> and address = #{address}</if>
|
<if test="address != null and address != ''"> and address = #{address}</if>
|
||||||
<if test="phone != null and phone != ''"> and phone = #{phone}</if>
|
<if test="phone != null and phone != ''"> and phone like concat('%', #{phone}, '%')</if>
|
||||||
<if test="email != null and email != ''"> and email = #{email}</if>
|
<if test="email != null and email != ''"> and email = #{email}</if>
|
||||||
<if test="wechat != null and wechat != ''"> and wechat = #{wechat}</if>
|
<if test="wechat != null and wechat != ''"> and wechat = #{wechat}</if>
|
||||||
<if test="type != null "> and type = #{type}</if>
|
<if test="type != null "> and type = #{type}</if>
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
|
<if test="updatedAt != null "> and updated_at = #{updatedAt}</if>
|
||||||
<if test="invoicemoney != null "> and invoicemoney = #{invoicemoney}</if>
|
<if test="invoicemoney != null "> and invoicemoney = #{invoicemoney}</if>
|
||||||
<if test="invoicetext != null and invoicetext != ''"> and invoicetext = #{invoicetext}</if>
|
<if test="invoicetext != null and invoicetext != ''"> and invoicetext = #{invoicetext}</if>
|
||||||
<if test="orderid != null "> and orderid = #{orderid}</if>
|
<if test="orderid != null "> and orderid like concat('%', #{orderid}, '%')</if>
|
||||||
<if test="status != null "> and status = #{status}</if>
|
<if test="status != null "> and status = #{status}</if>
|
||||||
<if test="filedata != null and filedata != ''"> and filedata = #{filedata}</if>
|
<if test="filedata != null and filedata != ''"> and filedata = #{filedata}</if>
|
||||||
</where>
|
</where>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,18 @@ export function getProgram(id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询充值类目详细
|
||||||
|
export function getconfigconfigone() {
|
||||||
|
return request({
|
||||||
|
url: '/api/public/config/config_one',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 新增充值类目
|
// 新增充值类目
|
||||||
export function addProgram(data) {
|
export function addProgram(data) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ export function listUsers(query) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 查询用户列表详细
|
// 查询用户列表详细
|
||||||
export function getUsers(id) {
|
export function getUsers(id) {
|
||||||
return request({
|
return request({
|
||||||
|
|
@ -53,3 +55,21 @@ export function delUsers(id) {
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:获取用户消费金/服务金变动记录
|
||||||
|
export function getBenefitLogs(userId, type, params) {
|
||||||
|
return request({
|
||||||
|
url: `/system/users/benefitLogs/${userId}/${type}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户余额变动记录
|
||||||
|
export function getBalanceLogs(userId, params) {
|
||||||
|
return request({
|
||||||
|
url: `/system/users/balanceLogs/${userId}`,
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* 金额格式化工具
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 金额格式化方法
|
||||||
|
export function formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金额样式配置
|
||||||
|
export const moneyStyles = {
|
||||||
|
// 主要金额样式(橙色)
|
||||||
|
primary: 'color: #E6A23C; font-weight: bold; font-size: 14px;',
|
||||||
|
// 收入/正数样式(绿色)
|
||||||
|
income: 'color: #67C23A; font-weight: bold; font-size: 14px;',
|
||||||
|
// 支出/负数样式(红色)
|
||||||
|
expense: 'color: #F56C6C; font-weight: bold; font-size: 14px;',
|
||||||
|
// 禁用/空值样式(灰色)
|
||||||
|
disabled: 'color: #C0C4CC;',
|
||||||
|
// 小号金额样式
|
||||||
|
small: 'color: #E6A23C; font-weight: bold; font-size: 12px;'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Vue混入对象
|
||||||
|
export const moneyMixin = {
|
||||||
|
methods: {
|
||||||
|
formatMoney(value) {
|
||||||
|
return formatMoney(value);
|
||||||
|
},
|
||||||
|
// 获取金额显示样式
|
||||||
|
getMoneyStyle(value, type = 'primary') {
|
||||||
|
if (type === 'balance') {
|
||||||
|
return value > 0 ? moneyStyles.income : (value < 0 ? moneyStyles.expense : moneyStyles.primary);
|
||||||
|
}
|
||||||
|
return moneyStyles[type] || moneyStyles.primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -115,8 +115,21 @@
|
||||||
<el-table-column label="地址" align="center" prop="userAddress" />
|
<el-table-column label="地址" align="center" prop="userAddress" />
|
||||||
<el-table-column label="商品" align="center" prop="productId" />
|
<el-table-column label="商品" align="center" prop="productId" />
|
||||||
<el-table-column label="数量" align="center" prop="num" />
|
<el-table-column label="数量" align="center" prop="num" />
|
||||||
<el-table-column label="单价" align="center" prop="price" />
|
<el-table-column label="单价" align="center" prop="price">
|
||||||
<el-table-column label="总积分" align="center" prop="totalPrice" />
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="总积分" align="center" prop="totalPrice">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #FF6B35; font-weight: bold; font-size: 14px;">
|
||||||
|
<i class="el-icon-star-on" style="color: #FFD700;"></i>
|
||||||
|
{{ scope.row.totalPrice }}积分
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="status" />
|
<el-table-column label="状态" align="center" prop="status" />
|
||||||
|
|
||||||
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
||||||
|
|
@ -196,6 +209,7 @@
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="单价" prop="price">
|
<el-form-item label="单价" prop="price">
|
||||||
|
<div style="position: relative;">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="form.price"
|
v-model="form.price"
|
||||||
:min="0"
|
:min="0"
|
||||||
|
|
@ -205,8 +219,15 @@
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入单价"
|
placeholder="请输入单价"
|
||||||
/>
|
/>
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
|
||||||
|
<i class="el-icon-info" style="color: #E6A23C;"></i>
|
||||||
|
商品市场价格
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="支付积分" prop="totalPrice">
|
<el-form-item label="支付积分" prop="totalPrice">
|
||||||
|
<div style="position: relative;">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="form.totalPrice"
|
v-model="form.totalPrice"
|
||||||
:min="0"
|
:min="0"
|
||||||
|
|
@ -215,6 +236,20 @@
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入支付积分"
|
placeholder="请输入支付积分"
|
||||||
/>
|
/>
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #FF6B35; font-weight: bold; pointer-events: none;">
|
||||||
|
<i class="el-icon-star-on" style="color: #FFD700;"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 5px; color: #909399; font-size: 12px;">
|
||||||
|
<i class="el-icon-info" style="color: #FF6B35;"></i>
|
||||||
|
用户需要支付的积分数量
|
||||||
|
</div>
|
||||||
|
<div v-if="form.price && form.totalPrice" style="margin-top: 5px;">
|
||||||
|
<el-tag size="small" type="success">
|
||||||
|
<i class="el-icon-discount"></i>
|
||||||
|
积分抵扣 ¥{{ formatMoney(form.price) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.status">
|
||||||
|
|
@ -265,9 +300,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { listIntegralOrder, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder,getIntegralProductList,getSiteDeliveryList } from "@/api/system/IntegralOrder"
|
import { listIntegralOrder, getIntegralOrder, delIntegralOrder, addIntegralOrder, updateIntegralOrder,getIntegralProductList,getSiteDeliveryList } from "@/api/system/IntegralOrder"
|
||||||
import request from "@/utils/request"
|
import request from "@/utils/request"
|
||||||
|
import UserSelect from "@/components/UserSelect"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "IntegralOrder",
|
name: "IntegralOrder",
|
||||||
|
components: {
|
||||||
|
UserSelect
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
|
|
@ -510,6 +549,13 @@ export default {
|
||||||
this.download('system/IntegralOrder/export', {
|
this.download('system/IntegralOrder/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `IntegralOrder_${new Date().getTime()}.xlsx`)
|
}, `IntegralOrder_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
/** 格式化金额显示 */
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@
|
||||||
<el-table-column label="订单ID" align="center" prop="ocode" />
|
<el-table-column label="订单ID" align="center" prop="ocode" />
|
||||||
<el-table-column label="金额" width="120px" align="left" prop="price" >
|
<el-table-column label="金额" width="120px" align="left" prop="price" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span >¥{{scope.row.price.toFixed(2)}}</span>
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="备注" align="center" prop="mark" />
|
<el-table-column label="备注" align="center" prop="mark" />
|
||||||
|
|
@ -90,7 +92,10 @@
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="金额" prop="price">
|
<el-form-item label="金额" prop="price">
|
||||||
|
<div style="position: relative;">
|
||||||
<el-input v-model="form.price" placeholder="请输入金额" />
|
<el-input v-model="form.price" placeholder="请输入金额" />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="mark">
|
<el-form-item label="备注" prop="mark">
|
||||||
<el-input v-model="form.mark" placeholder="请输入备注" />
|
<el-input v-model="form.mark" placeholder="请输入备注" />
|
||||||
|
|
@ -293,6 +298,13 @@ export default {
|
||||||
this.download('system/PayMoneyLog/export', {
|
this.download('system/PayMoneyLog/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `PayMoneyLog_${new Date().getTime()}.xlsx`)
|
}, `PayMoneyLog_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
// 格式化金额
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,13 @@
|
||||||
<el-table-column label="服务" align="center" prop="serviceName" />
|
<el-table-column label="服务" align="center" prop="serviceName" />
|
||||||
|
|
||||||
<el-table-column label="标题" align="center" prop="title" />
|
<el-table-column label="标题" align="center" prop="title" />
|
||||||
<el-table-column label="价格" align="center" width="75" prop="price" />
|
<el-table-column label="价格" align="center" width="75" prop="price">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="单位" align="center" width="75" prop="unit" />
|
<el-table-column label="单位" align="center" width="75" prop="unit" />
|
||||||
<el-table-column label="创建时间" align="center" prop="createdAt" width="130">
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="130">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -148,7 +154,10 @@
|
||||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="价格" prop="price">
|
<el-form-item label="价格" prop="price">
|
||||||
<el-input-number v-model="form.price" :min="0" :step="0.01" :precision="2" placeholder="请输入价格" style="width: 100%" />
|
<div style="position: relative;">
|
||||||
|
<el-input v-model="form.price" placeholder="请输入价格" />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="单位" prop="unit">
|
<el-form-item label="单位" prop="unit">
|
||||||
<el-input v-model="form.unit" placeholder="请输入单位" />
|
<el-input v-model="form.unit" placeholder="请输入单位" />
|
||||||
|
|
@ -401,6 +410,12 @@ export default {
|
||||||
this.download('system/QuoteCraft/export', {
|
this.download('system/QuoteCraft/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `QuoteCraft_${new Date().getTime()}.xlsx`)
|
}, `QuoteCraft_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return Number(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,13 @@
|
||||||
<el-table-column label="服务项目" align="center" prop="serviceName" />
|
<el-table-column label="服务项目" align="center" prop="serviceName" />
|
||||||
<el-table-column label="类型" align="center" prop="typeName" />
|
<el-table-column label="类型" align="center" prop="typeName" />
|
||||||
<el-table-column label="标题" align="center" width="175" prop="title" />
|
<el-table-column label="标题" align="center" width="175" prop="title" />
|
||||||
<el-table-column label="价格" align="center" width="75" prop="price" />
|
<el-table-column label="价格" align="center" width="75" prop="price">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="单位" align="center" width="115" prop="unit" />
|
<el-table-column label="单位" align="center" width="115" prop="unit" />
|
||||||
<el-table-column label="创建时间" align="center" prop="createdAt" width="150">
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="150">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -181,7 +187,10 @@
|
||||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="价格" prop="price">
|
<el-form-item label="价格" prop="price">
|
||||||
<el-input-number v-model="form.price" :min="0" :step="0.01" :precision="2" placeholder="请输入价格" style="width: 100%" />
|
<div style="position: relative;">
|
||||||
|
<el-input v-model="form.price" placeholder="请输入价格" />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="单位" prop="unit">
|
<el-form-item label="单位" prop="unit">
|
||||||
<el-input v-model="form.unit" placeholder="请输入单位" />
|
<el-input v-model="form.unit" placeholder="请输入单位" />
|
||||||
|
|
@ -475,6 +484,12 @@ export default {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -83,21 +83,53 @@
|
||||||
<image-preview :src="scope.row.avatar" :width="50" :height="50"/>
|
<image-preview :src="scope.row.avatar" :width="50" :height="50"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="会员状态" align="center" prop="ismember">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ scope.row.ismember === 1 ? '是' : '否' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="会员开始时间" align="center" prop="memberBegin" width="160">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.memberBegin, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="会员结束时间" align="center" prop="memberEnd" width="160">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.memberEnd, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="消费金" align="center" prop="consumption">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="showBenefitLogs(scope.row.id, 2)">
|
||||||
|
¥{{ parseFloat(scope.row.consumption || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="服务金" align="center" prop="servicefee">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="showBenefitLogs(scope.row.id, 1)" style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.servicefee) }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="余额" align="center" prop="balance">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" @click="showBalanceLogs(scope.row.id)" :style="{ color: scope.row.balance > 0 ? '#67C23A' : '#E6A23C', fontWeight: 'bold', fontSize: '14px' }">
|
||||||
|
¥{{ formatMoney(scope.row.balance) }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="状态" align="center" prop="status">
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.users_status" :value="scope.row.status"/>
|
<dict-tag :options="dict.type.users_status" :value="scope.row.status"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="160">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="更新时间" align="center" prop="updatedAt" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span>{{ parseTime(scope.row.updatedAt, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
|
@ -128,7 +160,7 @@
|
||||||
|
|
||||||
<!-- 添加或修改用户列表对话框 -->
|
<!-- 添加或修改用户列表对话框 -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
|
||||||
<el-form-item label="ID" v-if="form.id">
|
<el-form-item label="ID" v-if="form.id">
|
||||||
<el-input v-model="form.id" disabled />
|
<el-input v-model="form.id" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -192,6 +224,19 @@
|
||||||
placeholder="自动获取">
|
placeholder="自动获取">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="生日" prop="birthday">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.birthday"
|
||||||
|
type="date"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
placeholder="生日">
|
||||||
|
</el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
|
@ -199,15 +244,270 @@
|
||||||
<el-button icon="el-icon-refresh" @click="resetForm('form')">重置</el-button>
|
<el-button icon="el-icon-refresh" @click="resetForm('form')">重置</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 日志记录弹窗 -->
|
||||||
|
<el-dialog :title="logTitle" :visible.sync="logVisible" width="1000px" append-to-body>
|
||||||
|
<!-- 统计信息卡片 -->
|
||||||
|
<el-row :gutter="16" class="mb8">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" class="summary-card income-card">
|
||||||
|
<div class="summary-content">
|
||||||
|
<div class="summary-icon">
|
||||||
|
<i class="el-icon-plus"></i>
|
||||||
|
</div>
|
||||||
|
<div class="summary-info">
|
||||||
|
<div class="summary-title">总收入</div>
|
||||||
|
<div class="summary-value">¥{{ formatMoney(totalIncome) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" class="summary-card expense-card">
|
||||||
|
<div class="summary-content">
|
||||||
|
<div class="summary-icon">
|
||||||
|
<i class="el-icon-minus"></i>
|
||||||
|
</div>
|
||||||
|
<div class="summary-info">
|
||||||
|
<div class="summary-title">总支出</div>
|
||||||
|
<div class="summary-value">¥{{ formatMoney(totalExpense) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-card shadow="hover" class="summary-card net-card">
|
||||||
|
<div class="summary-content">
|
||||||
|
<div class="summary-icon">
|
||||||
|
<i class="el-icon-d-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
<div class="summary-info">
|
||||||
|
<div class="summary-title">净收入</div>
|
||||||
|
<div class="summary-value" :class="{ 'negative': totalIncome - totalExpense < 0 }">
|
||||||
|
¥{{ formatMoney(totalIncome - totalExpense) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 筛选工具栏 -->
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-select v-model="logFilter.type" placeholder="变动类型" clearable @change="filterLogs">
|
||||||
|
<el-option label="全部" value=""></el-option>
|
||||||
|
<el-option label="收入" value="1"></el-option>
|
||||||
|
<el-option label="支出" value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6" v-if="currentType === null">
|
||||||
|
<el-select v-model="logFilter.consumptionType" placeholder="消费类别" clearable @change="filterLogs">
|
||||||
|
<el-option label="全部" value=""></el-option>
|
||||||
|
<el-option label="商城消费" value="0"></el-option>
|
||||||
|
<el-option label="服务类消费" value="1"></el-option>
|
||||||
|
<el-option label="其他消费" value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="logFilter.dateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
@change="filterLogs"
|
||||||
|
>
|
||||||
|
</el-date-picker>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-button type="primary" icon="el-icon-search" @click="filterLogs">筛选</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table
|
||||||
|
v-loading="logLoading"
|
||||||
|
:data="filteredLogList"
|
||||||
|
style="width: 100%"
|
||||||
|
:row-class-name="tableRowClassName"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
|
||||||
|
<el-table-column label="变动类型" align="center" width="80">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getChangeTypeTag(scope.row)"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ getChangeTypeText(scope.row) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="变动金额" align="center" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span
|
||||||
|
:class="{
|
||||||
|
'amount-positive': getAmountValue(scope.row) > 0,
|
||||||
|
'amount-negative': getAmountValue(scope.row) < 0
|
||||||
|
}"
|
||||||
|
class="amount-text"
|
||||||
|
>
|
||||||
|
{{ getAmountValue(scope.row) > 0 ? '+' : '' }}¥{{ Math.abs(getAmountValue(scope.row)).toFixed(2) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="变动前金额" align="center" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="amount-text">¥{{ getBeforeAmount(scope.row) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="变动后金额" align="center" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="amount-text">¥{{ getAfterAmount(scope.row) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="关联订单" align="center" width="100" v-if="currentType !== null">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.orderid"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="viewOrder(scope.row.orderid)"
|
||||||
|
>
|
||||||
|
#{{ scope.row.orderid }}
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="订单金额" align="center" width="100" v-if="currentType !== null">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.ordermoney">¥{{ parseFloat(scope.row.ordermoney).toFixed(2) }}</span>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="消费类别" align="center" width="100" v-if="currentType === null">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="getConsumptionTypeTag(scope.row.consumptiontype)"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ getConsumptionTypeText(scope.row.consumptiontype) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="变动说明" align="center" prop="reamk" min-width="200" :show-overflow-tooltip="true">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="remark-content">
|
||||||
|
<i class="el-icon-info"></i>
|
||||||
|
{{ scope.row.reamk || scope.row.remark || '无备注' }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="变动时间" align="center" width="160">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div class="time-content">
|
||||||
|
<i class="el-icon-time"></i>
|
||||||
|
{{ formatDateTime(scope.row) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="操作" align="center" width="80" fixed="right">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-view"
|
||||||
|
@click="viewLogDetail(scope.row)"
|
||||||
|
>
|
||||||
|
详情
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<pagination
|
||||||
|
v-show="logTotal>0"
|
||||||
|
:total="logTotal"
|
||||||
|
:page.sync="logQuery.pageNum"
|
||||||
|
:limit.sync="logQuery.pageSize"
|
||||||
|
@pagination="getLogList"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="exportLogs" type="primary" plain>
|
||||||
|
<i class="el-icon-download"></i>
|
||||||
|
导出记录
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="logVisible = false">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 日志详情弹窗 -->
|
||||||
|
<el-dialog title="记录详情" :visible.sync="logDetailVisible" width="600px" append-to-body>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="记录ID">{{ logDetail.id }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动类型">
|
||||||
|
<el-tag :type="getChangeTypeTag(logDetail)" size="small">
|
||||||
|
{{ getChangeTypeText(logDetail) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动金额">
|
||||||
|
<span :class="{
|
||||||
|
'amount-positive': getAmountValue(logDetail) > 0,
|
||||||
|
'amount-negative': getAmountValue(logDetail) < 0
|
||||||
|
}">
|
||||||
|
{{ getAmountValue(logDetail) > 0 ? '+' : '' }}¥{{ Math.abs(getAmountValue(logDetail)).toFixed(2) }}
|
||||||
|
</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动前金额">
|
||||||
|
¥{{ getBeforeAmount(logDetail) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动后金额">
|
||||||
|
¥{{ getAfterAmount(logDetail) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="关联订单" v-if="logDetail.orderid">
|
||||||
|
<el-button type="text" @click="viewOrder(logDetail.orderid)">#{{ logDetail.orderid }}</el-button>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="订单金额" v-if="logDetail.ordermoney">
|
||||||
|
¥{{ parseFloat(logDetail.ordermoney).toFixed(2) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="消费类别" v-if="logDetail.consumptiontype !== undefined">
|
||||||
|
<el-tag :type="getConsumptionTypeTag(logDetail.consumptiontype)" size="small">
|
||||||
|
{{ getConsumptionTypeText(logDetail.consumptiontype) }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动时间" :span="2">
|
||||||
|
{{ formatDateTime(logDetail) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="变动说明" :span="2">
|
||||||
|
{{ logDetail.reamk || logDetail.remark || '无备注' }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listUsers, getUsers, delUsers, addUsers, updateUsers,getUserDataList } from "@/api/system/users"
|
import { listUsers, getUsers, delUsers, addUsers, updateUsers, getUserDataList, getBenefitLogs, getBalanceLogs } from "@/api/system/users"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Users",
|
name: "Users",
|
||||||
dicts: ['users_status'],
|
dicts: ['users_status', 'sys_yes_no'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
|
|
@ -239,7 +539,15 @@ export default {
|
||||||
status: null,
|
status: null,
|
||||||
},
|
},
|
||||||
// 表单参数
|
// 表单参数
|
||||||
form: {},
|
form: {
|
||||||
|
birthday: null,
|
||||||
|
ismember: 0,
|
||||||
|
member_begin: null,
|
||||||
|
member_end: null,
|
||||||
|
consumption: 0,
|
||||||
|
servicefee: 0,
|
||||||
|
balance: 0
|
||||||
|
},
|
||||||
// 表单校验
|
// 表单校验
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
|
|
@ -251,7 +559,47 @@ export default {
|
||||||
status: [
|
status: [
|
||||||
{ required: true, message: "1:启用 0:关闭不能为空", trigger: "change" }
|
{ required: true, message: "1:启用 0:关闭不能为空", trigger: "change" }
|
||||||
],
|
],
|
||||||
}
|
ismember: [
|
||||||
|
{ required: true, message: "请选择会员状态", trigger: "change" }
|
||||||
|
],
|
||||||
|
consumption: [
|
||||||
|
{ required: true, message: "消费金不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
servicefee: [
|
||||||
|
{ required: true, message: "服务金不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
balance: [
|
||||||
|
{ required: true, message: "余额不能为空", trigger: "blur" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 日志记录相关
|
||||||
|
logVisible: false,
|
||||||
|
logLoading: false,
|
||||||
|
logTitle: '',
|
||||||
|
logList: [],
|
||||||
|
logTotal: 0,
|
||||||
|
currentUserId: null,
|
||||||
|
currentType: null,
|
||||||
|
logQuery: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10
|
||||||
|
},
|
||||||
|
// 日志详情弹窗
|
||||||
|
logDetailVisible: false,
|
||||||
|
logDetail: {},
|
||||||
|
// 筛选条件
|
||||||
|
logFilter: {
|
||||||
|
type: '',
|
||||||
|
consumptionType: '',
|
||||||
|
dateRange: []
|
||||||
|
},
|
||||||
|
// 统计数据
|
||||||
|
totalIncome: 0,
|
||||||
|
totalExpense: 0,
|
||||||
|
// 原始日志数据
|
||||||
|
originalLogList: [],
|
||||||
|
// 筛选后的日志数据
|
||||||
|
filteredLogList: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -324,7 +672,9 @@ export default {
|
||||||
isStop: null,
|
isStop: null,
|
||||||
middleAuth: null,
|
middleAuth: null,
|
||||||
createdAt: null,
|
createdAt: null,
|
||||||
updatedAt: null
|
updatedAt: null,
|
||||||
|
birthday: null,
|
||||||
|
balance: 0
|
||||||
}
|
}
|
||||||
this.resetForm("form")
|
this.resetForm("form")
|
||||||
},
|
},
|
||||||
|
|
@ -397,6 +747,243 @@ export default {
|
||||||
this.download('system/users/export', {
|
this.download('system/users/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `users_${new Date().getTime()}.xlsx`)
|
}, `users_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
/** 显示日志记录 */
|
||||||
|
showBenefitLogs(userId, type) {
|
||||||
|
this.currentUserId = userId;
|
||||||
|
this.currentType = type;
|
||||||
|
this.logTitle = type === 1 ? '服务金变动记录' : '消费金变动记录';
|
||||||
|
this.logVisible = true;
|
||||||
|
this.resetLogFilters();
|
||||||
|
this.getLogList();
|
||||||
|
},
|
||||||
|
/** 获取日志列表 */
|
||||||
|
getLogList() {
|
||||||
|
this.logLoading = true;
|
||||||
|
getBenefitLogs(this.currentUserId, this.currentType, {
|
||||||
|
pageNum: this.logQuery.pageNum,
|
||||||
|
pageSize: this.logQuery.pageSize
|
||||||
|
}).then(response => {
|
||||||
|
this.logList = response.rows;
|
||||||
|
this.originalLogList = response.rows;
|
||||||
|
this.logTotal = response.total;
|
||||||
|
this.calculateStatistics();
|
||||||
|
this.applyFilters();
|
||||||
|
this.logLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.logLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 获取余额变动日志列表 */
|
||||||
|
getBalanceLogList() {
|
||||||
|
this.logLoading = true;
|
||||||
|
getBalanceLogs(this.currentUserId, {
|
||||||
|
pageNum: this.logQuery.pageNum,
|
||||||
|
pageSize: this.logQuery.pageSize
|
||||||
|
}).then(response => {
|
||||||
|
this.logList = response.rows;
|
||||||
|
this.originalLogList = response.rows;
|
||||||
|
this.logTotal = response.total;
|
||||||
|
this.calculateStatistics();
|
||||||
|
this.applyFilters();
|
||||||
|
this.logLoading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.logLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** 计算统计数据 */
|
||||||
|
calculateStatistics() {
|
||||||
|
let income = 0;
|
||||||
|
let expense = 0;
|
||||||
|
|
||||||
|
this.originalLogList.forEach(item => {
|
||||||
|
const amount = this.getAmountValue(item);
|
||||||
|
if (amount > 0) {
|
||||||
|
income += amount;
|
||||||
|
} else {
|
||||||
|
expense += Math.abs(amount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.totalIncome = income;
|
||||||
|
this.totalExpense = expense;
|
||||||
|
},
|
||||||
|
/** 应用筛选条件 */
|
||||||
|
applyFilters() {
|
||||||
|
let filtered = [...this.originalLogList];
|
||||||
|
|
||||||
|
// 按类型筛选
|
||||||
|
if (this.logFilter.type) {
|
||||||
|
filtered = filtered.filter(item => {
|
||||||
|
const amount = this.getAmountValue(item);
|
||||||
|
if (this.logFilter.type === '1') {
|
||||||
|
return amount > 0; // 收入
|
||||||
|
} else if (this.logFilter.type === '2') {
|
||||||
|
return amount < 0; // 支出
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按消费类别筛选(仅余额变动)
|
||||||
|
if (this.logFilter.consumptionType && this.currentType === null) {
|
||||||
|
filtered = filtered.filter(item => {
|
||||||
|
return item.consumptiontype === parseInt(this.logFilter.consumptionType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按日期范围筛选
|
||||||
|
if (this.logFilter.dateRange && this.logFilter.dateRange.length === 2) {
|
||||||
|
const startDate = new Date(this.logFilter.dateRange[0]);
|
||||||
|
const endDate = new Date(this.logFilter.dateRange[1]);
|
||||||
|
endDate.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
|
filtered = filtered.filter(item => {
|
||||||
|
const itemDate = new Date(item.createTime || item.createdAt || item.dotime || item.consumptiontime);
|
||||||
|
return itemDate >= startDate && itemDate <= endDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filteredLogList = filtered;
|
||||||
|
},
|
||||||
|
// 筛选日志
|
||||||
|
filterLogs() {
|
||||||
|
this.applyFilters();
|
||||||
|
},
|
||||||
|
// 导出日志
|
||||||
|
exportLogs() {
|
||||||
|
const params = {
|
||||||
|
userId: this.currentUserId,
|
||||||
|
type: this.currentType,
|
||||||
|
...this.logFilter,
|
||||||
|
pageNum: this.logQuery.pageNum,
|
||||||
|
pageSize: this.logQuery.pageSize
|
||||||
|
};
|
||||||
|
this.download('system/users/exportBenefitLogs', params, `${this.logTitle}_${new Date().getTime()}.xlsx`);
|
||||||
|
},
|
||||||
|
// 查看订单
|
||||||
|
viewOrder(orderId) {
|
||||||
|
this.$router.push({ path: '/order/detail', query: { id: orderId } });
|
||||||
|
},
|
||||||
|
// 查看日志详情
|
||||||
|
viewLogDetail(row) {
|
||||||
|
this.logDetail = row;
|
||||||
|
this.logDetailVisible = true;
|
||||||
|
},
|
||||||
|
// 获取变动类型标签
|
||||||
|
getChangeTypeTag(row) {
|
||||||
|
if (this.currentType === null) {
|
||||||
|
return 'info'; // 余额变动
|
||||||
|
}
|
||||||
|
if (row.type === 1) { // 服务金变动
|
||||||
|
return 'success';
|
||||||
|
} else { // 消费金变动
|
||||||
|
return 'danger';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取变动类型文本
|
||||||
|
getChangeTypeText(row) {
|
||||||
|
if (this.currentType === null) {
|
||||||
|
return '余额变动';
|
||||||
|
}
|
||||||
|
if (row.type === 1) {
|
||||||
|
return '服务金变动';
|
||||||
|
} else {
|
||||||
|
return '消费金变动';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取变动金额
|
||||||
|
getAmountValue(row) {
|
||||||
|
if (this.currentType === null) {
|
||||||
|
// 余额变动
|
||||||
|
if (row.consumptionmoney) {
|
||||||
|
return row.type === 1 ? row.consumptionmoney : -row.consumptionmoney;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 服务金/消费金变动
|
||||||
|
if (row.money) {
|
||||||
|
return row.ordertype === 1 ? row.money : -row.money;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
// 获取变动前金额
|
||||||
|
getBeforeAmount(row) {
|
||||||
|
if (this.currentType === null) {
|
||||||
|
// 余额变动
|
||||||
|
return parseFloat(row.beformoney || 0).toFixed(2);
|
||||||
|
}
|
||||||
|
// 服务金/消费金变动
|
||||||
|
return parseFloat(row.beformoney || 0).toFixed(2);
|
||||||
|
},
|
||||||
|
// 获取变动后金额
|
||||||
|
getAfterAmount(row) {
|
||||||
|
if (this.currentType === null) {
|
||||||
|
// 余额变动
|
||||||
|
return parseFloat(row.aftermoney || 0).toFixed(2);
|
||||||
|
}
|
||||||
|
// 服务金/消费金变动
|
||||||
|
return parseFloat(row.aftremoney || 0).toFixed(2);
|
||||||
|
},
|
||||||
|
// 获取消费类别标签
|
||||||
|
getConsumptionTypeTag(type) {
|
||||||
|
if (type === 0) {
|
||||||
|
return 'info'; // 商城消费
|
||||||
|
} else if (type === 1) {
|
||||||
|
return 'success'; // 服务类消费
|
||||||
|
} else {
|
||||||
|
return 'warning'; // 其他消费
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 获取消费类别文本
|
||||||
|
getConsumptionTypeText(type) {
|
||||||
|
if (type === 0) {
|
||||||
|
return '商城消费';
|
||||||
|
} else if (type === 1) {
|
||||||
|
return '服务类消费';
|
||||||
|
} else {
|
||||||
|
return '其他消费';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 格式化日期时间
|
||||||
|
formatDateTime(row) {
|
||||||
|
const dateTime = row.createTime || row.createdAt || row.dotime || row.consumptiontime;
|
||||||
|
return this.parseTime(dateTime, '{y}-{m}-{d} {h}:{i}:{s}');
|
||||||
|
},
|
||||||
|
/** 显示余额变动记录 */
|
||||||
|
showBalanceLogs(userId) {
|
||||||
|
this.currentUserId = userId;
|
||||||
|
this.currentType = null;
|
||||||
|
this.logTitle = '余额变动记录';
|
||||||
|
this.logVisible = true;
|
||||||
|
this.resetLogFilters();
|
||||||
|
this.getBalanceLogList();
|
||||||
|
},
|
||||||
|
/** 重置日志筛选条件 */
|
||||||
|
resetLogFilters() {
|
||||||
|
this.logFilter = {
|
||||||
|
type: '',
|
||||||
|
consumptionType: '',
|
||||||
|
dateRange: []
|
||||||
|
};
|
||||||
|
this.logQuery.pageNum = 1;
|
||||||
|
},
|
||||||
|
// 表格行样式
|
||||||
|
tableRowClassName({ row, rowIndex }) {
|
||||||
|
const amount = this.getAmountValue(row);
|
||||||
|
if (amount > 0) {
|
||||||
|
return 'success-row';
|
||||||
|
} else if (amount < 0) {
|
||||||
|
return 'warning-row';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
// 格式化金额
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -447,4 +1034,116 @@ export default {
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-button--text {
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.el-button--text:hover {
|
||||||
|
color: #409EFF;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 新增样式 */
|
||||||
|
.summary-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.income-card .summary-icon {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expense-card .summary-icon {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.net-card .summary-icon {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-positive {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-negative {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-content {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remark-content .el-icon-info {
|
||||||
|
margin-right: 5px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-content {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-content .el-icon-time {
|
||||||
|
margin-right: 5px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .el-table__row--striped td {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .el-table__row--striped:hover td {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .el-table__row--striped.el-table__row--striped--hover td {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -95,8 +95,25 @@
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="主键id" align="center" prop="id" />
|
<el-table-column label="主键id" align="center" prop="id" />
|
||||||
<el-table-column label="充值说明" align="center" prop="rechargename" />
|
<el-table-column label="充值说明" align="center" prop="rechargename" />
|
||||||
<el-table-column label="充值金额" align="center" prop="money" />
|
<el-table-column label="充值金额" align="center" prop="money">
|
||||||
<el-table-column label="优惠后金额" align="center" prop="discount" />
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.money) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="到账金额" align="center" prop="discount">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #67C23A; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.discount) }}
|
||||||
|
</span>
|
||||||
|
<div v-if="scope.row.discount > scope.row.money" style="margin-top: 2px;">
|
||||||
|
<el-tag size="mini" type="success">
|
||||||
|
+¥{{ formatMoney(scope.row.discount - scope.row.money) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="备注" align="center" prop="reamk" />
|
<el-table-column label="备注" align="center" prop="reamk" />
|
||||||
<el-table-column label="状态" align="center" prop="status">
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -148,16 +165,32 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 添加或修改充值类目对话框 -->
|
<!-- 添加或修改充值类目对话框 -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="30%" append-to-body>
|
<el-dialog :title="title" :visible.sync="open" width="35%" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||||
<el-form-item label="类目说明" prop="rechargename">
|
<el-form-item label="类目说明" prop="rechargename">
|
||||||
<el-input v-model="form.rechargename" placeholder="请输入类目说明" />
|
<el-input v-model="form.rechargename" placeholder="请输入类目说明" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="设定金额" prop="money">
|
<el-form-item label="设定金额" prop="money">
|
||||||
<el-input-number v-model="form.money" :min="0" :precision="2" :step="0.01" style="width: 100%" placeholder="请输入设定金额" />
|
<div style="position: relative;">
|
||||||
|
<el-input-number v-model="form.money" :min="0" :precision="2" :step="0.01" style="width: 100%" placeholder="请输入设定金额" @change="calculateDiscount"/>
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="优惠后金额" prop="discount">
|
<el-form-item label="到账金额" prop="discount">
|
||||||
<el-input-number v-model="form.discount" :min="0" :precision="2" :step="0.01" style="width: 100%" placeholder="请输入优惠后金额" />
|
<div style="position: relative;">
|
||||||
|
<el-input-number v-model="form.discount" :min="0" :precision="2" :step="0.01" style="width: 100%" placeholder="请输入优惠后金额" disabled />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #67C23A; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
|
<div class="el-form-item__description" style="color: #909399; font-size: 12px; margin-top: 5px;">
|
||||||
|
<i class="el-icon-info" style="color: #E6A23C;"></i>
|
||||||
|
系统设定到账金额为设定金额的<span style="color: #67C23A; font-weight: bold;">{{ rechargeDiscount }}%</span>优惠
|
||||||
|
</div>
|
||||||
|
<div v-if="form.money && form.discount > form.money" style="margin-top: 5px;">
|
||||||
|
<el-tag size="small" type="success">
|
||||||
|
<i class="el-icon-plus"></i>
|
||||||
|
额外赠送 ¥{{ formatMoney(form.discount - form.money) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="reamk">
|
<el-form-item label="备注" prop="reamk">
|
||||||
<el-input v-model="form.reamk" placeholder="请输入备注" />
|
<el-input v-model="form.reamk" placeholder="请输入备注" />
|
||||||
|
|
@ -193,7 +226,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listProgram, getProgram, delProgram, addProgram, updateProgram } from "@/api/system/program"
|
import { listProgram, getProgram, delProgram, addProgram, updateProgram, getconfigconfigone } from "@/api/system/program"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Program",
|
name: "Program",
|
||||||
|
|
@ -212,6 +245,9 @@ export default {
|
||||||
showSearch: true,
|
showSearch: true,
|
||||||
// 总条数
|
// 总条数
|
||||||
total: 0,
|
total: 0,
|
||||||
|
|
||||||
|
discount: 0,
|
||||||
|
rechargeDiscount: 0,
|
||||||
// 充值类目表格数据
|
// 充值类目表格数据
|
||||||
programList: [],
|
programList: [],
|
||||||
// 弹出层标题
|
// 弹出层标题
|
||||||
|
|
@ -253,6 +289,7 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getList()
|
this.getList()
|
||||||
|
this.getConfig()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** 查询充值类目列表 */
|
/** 查询充值类目列表 */
|
||||||
|
|
@ -264,6 +301,20 @@ export default {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
/** 获取配置信息 */
|
||||||
|
getConfig() {
|
||||||
|
getconfigconfigone().then(response => {
|
||||||
|
this.rechargeDiscount = response.data.recharge_discount || 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 计算优惠后金额 */
|
||||||
|
calculateDiscount() {
|
||||||
|
if (this.form.money) {
|
||||||
|
this.form.discount = Number((this.form.money + this.form.money * this.rechargeDiscount / 100).toFixed(2))
|
||||||
|
} else {
|
||||||
|
this.form.discount = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
cancel() {
|
cancel() {
|
||||||
this.open = false
|
this.open = false
|
||||||
|
|
@ -362,6 +413,13 @@ export default {
|
||||||
this.download('system/program/export', {
|
this.download('system/program/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `program_${new Date().getTime()}.xlsx`)
|
}, `program_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
/** 格式化金额显示 */
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,13 @@
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="${comment}" align="center" prop="id" />
|
<el-table-column label="${comment}" align="center" prop="id" />
|
||||||
<el-table-column label="订单id" align="center" prop="orderid" />
|
<el-table-column label="订单id" align="center" prop="orderid" />
|
||||||
<el-table-column label="报价金额" align="center" prop="money" />
|
<el-table-column label="报价金额" align="center" prop="money">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.money) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="师傅id" align="center" prop="workerid" />
|
<el-table-column label="师傅id" align="center" prop="workerid" />
|
||||||
<el-table-column label="师傅姓名" align="center" prop="workername" />
|
<el-table-column label="师傅姓名" align="center" prop="workername" />
|
||||||
<el-table-column label="师傅图像" align="center" prop="workerimage" width="100">
|
<el-table-column label="师傅图像" align="center" prop="workerimage" width="100">
|
||||||
|
|
@ -137,7 +143,10 @@
|
||||||
<el-input v-model="form.orderid" placeholder="请输入订单id" />
|
<el-input v-model="form.orderid" placeholder="请输入订单id" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="报价金额" prop="money">
|
<el-form-item label="报价金额" prop="money">
|
||||||
|
<div style="position: relative;">
|
||||||
<el-input v-model="form.money" placeholder="请输入报价金额" />
|
<el-input v-model="form.money" placeholder="请输入报价金额" />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="师傅id" prop="workerid">
|
<el-form-item label="师傅id" prop="workerid">
|
||||||
<el-input v-model="form.workerid" placeholder="请输入师傅id" />
|
<el-input v-model="form.workerid" placeholder="请输入师傅id" />
|
||||||
|
|
@ -325,6 +334,13 @@ export default {
|
||||||
this.download('system/quotation/export', {
|
this.download('system/quotation/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `quotation_${new Date().getTime()}.xlsx`)
|
}, `quotation_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
// 格式化金额
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,9 @@
|
||||||
<el-table-column label="用户" align="center" prop="uname" />
|
<el-table-column label="用户" align="center" prop="uname" />
|
||||||
<el-table-column label="金额" width="120px" align="left" prop="money" >
|
<el-table-column label="金额" width="120px" align="left" prop="money" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span >¥{{scope.row.money.toFixed(2)}}</span>
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.money) }}
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
|
|
@ -159,7 +161,10 @@
|
||||||
<el-input v-model="form.batchId" placeholder="请输入微信批次单号" />
|
<el-input v-model="form.batchId" placeholder="请输入微信批次单号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="金额" prop="money">
|
<el-form-item label="金额" prop="money">
|
||||||
|
<div style="position: relative;">
|
||||||
<el-input v-model="form.money" placeholder="请输入金额" />
|
<el-input v-model="form.money" placeholder="请输入金额" />
|
||||||
|
<span style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #E6A23C; font-weight: bold; pointer-events: none;">¥</span>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.status">
|
||||||
|
|
@ -392,6 +397,13 @@ export default {
|
||||||
this.download('system/transfer/export', {
|
this.download('system/transfer/export', {
|
||||||
...this.queryParams
|
...this.queryParams
|
||||||
}, `transfer_${new Date().getTime()}.xlsx`)
|
}, `transfer_${new Date().getTime()}.xlsx`)
|
||||||
|
},
|
||||||
|
// 金额格式化
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined || value === '') {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,17 +71,51 @@
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="ID" align="center" prop="id" />
|
<el-table-column label="ID" align="center" prop="id" />
|
||||||
<el-table-column label="订单号" align="center" prop="orderId" />
|
<el-table-column label="订单号" align="center" prop="orderId" />
|
||||||
<el-table-column label="金额" align="center" prop="price" />
|
<el-table-column label="金额" align="center" prop="price">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="收支类别" align="center" prop="type">
|
<el-table-column label="收支类别" align="center" prop="type">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.worker_money_type" :value="scope.row.type"/>
|
<dict-tag :options="dict.type.worker_money_type" :value="scope.row.type"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="服务金额" align="center" prop="servicePrice" />
|
<el-table-column label="服务金额" align="center" prop="servicePrice">
|
||||||
<el-table-column label="优惠金额" align="center" prop="reductionPrice" />
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.servicePrice" style="color: #E6A23C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.servicePrice) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="优惠金额" align="center" prop="reductionPrice">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.reductionPrice" style="color: #F56C6C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.reductionPrice) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="提成比例" align="center" prop="cr" />
|
<el-table-column label="提成比例" align="center" prop="cr" />
|
||||||
<el-table-column label="质保金" align="center" prop="mergin" />
|
<el-table-column label="质保金" align="center" prop="mergin">
|
||||||
<el-table-column label="上门费" align="center" prop="doorPrice" />
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.mergin" style="color: #E6A23C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.mergin) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="上门费" align="center" prop="doorPrice">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.doorPrice" style="color: #E6A23C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.doorPrice) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d}') }}</span>
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,13 @@
|
||||||
<el-table-column label="ID" align="center" prop="id" />
|
<el-table-column label="ID" align="center" prop="id" />
|
||||||
<el-table-column label="师傅姓名" align="center" prop="workerName" />
|
<el-table-column label="师傅姓名" align="center" prop="workerName" />
|
||||||
<el-table-column label="订单号" align="center" prop="orderId" />
|
<el-table-column label="订单号" align="center" prop="orderId" />
|
||||||
<el-table-column label="金额" align="center" prop="price" />
|
<el-table-column label="金额" align="center" prop="price">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="收支类别" align="center" prop="type">
|
<el-table-column label="收支类别" align="center" prop="type">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.worker_money_type" :value="scope.row.type"/>
|
<dict-tag :options="dict.type.worker_money_type" :value="scope.row.type"/>
|
||||||
|
|
@ -169,8 +175,22 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="锁单天数" align="center" prop="lookday" />
|
<el-table-column label="锁单天数" align="center" prop="lookday" />
|
||||||
|
|
||||||
<el-table-column label="锁单金额" align="center" prop="lookMoney" />
|
<el-table-column label="锁单金额" align="center" prop="lookMoney">
|
||||||
<el-table-column label="调整金额" align="center" prop="adminUpPrice" />
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.lookMoney" style="color: #E6A23C; font-weight: bold;">
|
||||||
|
¥{{ formatMoney(scope.row.lookMoney) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="调整金额" align="center" prop="adminUpPrice">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.adminUpPrice" :style="{ color: scope.row.adminUpPrice > 0 ? '#67C23A' : '#F56C6C', fontWeight: 'bold' }">
|
||||||
|
{{ scope.row.adminUpPrice > 0 ? '+' : '' }}¥{{ formatMoney(Math.abs(scope.row.adminUpPrice)) }}
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #C0C4CC;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="锁单调整原由" align="center" prop="adminUpReamk" />
|
<el-table-column label="锁单调整原由" align="center" prop="adminUpReamk" />
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
|
|
@ -378,11 +398,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { listWorkerMoneyLog, getWorkerMoneyLog, delWorkerMoneyLog, addWorkerMoneyLog, updateWorkerMoneyLog, lockWorkerMoney, unlockWorkerMoney, adjustWorkerMoney } from "@/api/system/workerMoneyLog"
|
import { listWorkerMoneyLog, getWorkerMoneyLog, delWorkerMoneyLog, addWorkerMoneyLog, updateWorkerMoneyLog, lockWorkerMoney, unlockWorkerMoney, adjustWorkerMoney } from "@/api/system/workerMoneyLog"
|
||||||
import WorkerMoneyLogEdit from './WorkerMoneyLogEdit.vue'
|
import WorkerMoneyLogEdit from './WorkerMoneyLogEdit.vue'
|
||||||
|
import UserSelect from "@/components/UserSelect"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "WorkerMoneyLog",
|
name: "WorkerMoneyLog",
|
||||||
dicts: ['worker_money_type', 'worker_money_status_type', 'worker_money_status'],
|
dicts: ['worker_money_type', 'worker_money_status_type', 'worker_money_status'],
|
||||||
components: {WorkerMoneyLogEdit},
|
components: {
|
||||||
|
WorkerMoneyLogEdit,
|
||||||
|
UserSelect
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
|
|
@ -692,6 +716,12 @@ export default {
|
||||||
this.$modal.closeLoading();
|
this.$modal.closeLoading();
|
||||||
this.$message.error('操作失败');
|
this.$message.error('操作失败');
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
formatMoney(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return '0.00';
|
||||||
|
}
|
||||||
|
return parseFloat(value).toFixed(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue