202507051753

This commit is contained in:
张潘 2025-07-05 17:53:31 +08:00
parent faf16add2c
commit 1a8a62321a
23 changed files with 3549 additions and 1826 deletions

View File

@ -16,6 +16,7 @@
<ruoyi.version>3.8.9</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-boot.version>2.5.15</spring-boot.version>

View File

@ -13,7 +13,7 @@ ruoyi:
# 验证码类型 math 数字计算 char 字符验证
captchaType: math
wechat:
appid: wx1234567890123456
appid: wx73d0202b3c8a6d68
mchid: 1672571923
apikey: sssssssssssssssssssssssssssssssS
certpath: wechat/apiclient_cert.p12

View File

@ -0,0 +1,461 @@
package com.ruoyi.system.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.ControllerUtil.AppletControllerUtil;
import com.ruoyi.system.ControllerUtil.AppletLoginUtil;
import com.ruoyi.system.ControllerUtil.InvoiceUtil;
import com.ruoyi.system.domain.Users;
import com.ruoyi.system.service.IUsersService;
import com.ruoyi.system.service.IUsersInvoiceInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 发票管理控制器
*
* 提供发票相关的API接口包括
* 1. 发票中心数据获取
* 2. 待开票订单查询
* 3. 已开票记录查询
* 4. 批量开票功能
* 5. 发票信息管理
* 6. 发票统计数据
*
* @author Mr. Zhang Pan
* @version 1.0
* @date 2025-01-26
*/
@RestController
@RequestMapping("/api/user/invoice")
public class AppleInvoiceController extends BaseController {
@Autowired
private IUsersService usersService;
@Autowired
private IUsersInvoiceInfoService usersInvoiceInfoService;
private ObjectMapper objectMapper = new ObjectMapper();
/**
* 获取用户发票中心数据
*
* @param params 请求参数
* @param request HTTP请求对象
* @return 发票中心数据
*/
@PostMapping("/center")
public AjaxResult getUserInvoiceCenter(@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.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取分页参数
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
int limit = params.get("limit") != null ? Integer.parseInt(params.get("limit").toString()) : 10;
// 4. 获取发票中心数据
Map<String, Object> invoiceCenterData = InvoiceUtil.getUserInvoiceCenter(user.getId(), page, limit);
return AppletControllerUtil.appletSuccess(invoiceCenterData);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取发票中心数据失败:" + e.getMessage());
}
}
/**
* 获取待开票订单列表
*
* @param params 请求参数
* @param request HTTP请求对象
* @return 待开票订单列表
*/
@PostMapping("/pending")
public AjaxResult getPendingInvoiceOrders(@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.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取分页参数
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
int limit = params.get("limit") != null ? Integer.parseInt(params.get("limit").toString()) : 10;
// 4. 获取待开票订单列表
List<Map<String, Object>> pendingOrders = InvoiceUtil.getPendingInvoiceOrders(user.getId(), page, limit);
return AppletControllerUtil.appletSuccess(pendingOrders);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取待开票订单列表失败:" + e.getMessage());
}
}
/**
* 获取已开票列表
*
* @param params 请求参数
* @param request HTTP请求对象
* @return 已开票列表
*/
@PostMapping("/completed")
public AjaxResult getCompletedInvoices(@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.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取分页参数
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
int limit = params.get("limit") != null ? Integer.parseInt(params.get("limit").toString()) : 10;
// 4. 获取已开票列表
List<Map<String, Object>> completedInvoices = InvoiceUtil.getCompletedInvoices(user.getId(), page, limit);
return AppletControllerUtil.appletSuccess(completedInvoices);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取已开票列表失败:" + e.getMessage());
}
}
/**
* 开具发票接口支持批量开票
*
* @param params 发票参数
* @param request HTTP请求对象
* @return 开票结果
*/
@PostMapping("/create")
public AjaxResult createInvoice(@RequestBody Map<String, Object> params, HttpServletRequest request) {
try {
// 调试日志输出接收到的原始参数
System.out.println("接收到的原始参数: " + params);
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 验证必要参数
List<String> orderIds = new ArrayList<>();
// 支持多种参数格式orderIds数组orderIds字符串数组单个orderId
if (params.get("orderIds") != null) {
Object orderIdsObj = params.get("orderIds");
if (orderIdsObj instanceof List) {
// 批量开票orderIds数组
List<?> orderIdList = (List<?>) orderIdsObj;
for (Object orderIdObj : orderIdList) {
if (orderIdObj != null) {
orderIds.add(orderIdObj.toString());
}
}
} else if (orderIdsObj instanceof String) {
// 处理字符串格式的数组 "['B1750646653464697','B1750413502322711']"
String orderIdsStr = orderIdsObj.toString();
// 去掉外层的方括号和引号
orderIdsStr = orderIdsStr.trim();
if (orderIdsStr.startsWith("[") && orderIdsStr.endsWith("]")) {
orderIdsStr = orderIdsStr.substring(1, orderIdsStr.length() - 1);
}
// 按逗号分割并清理每个订单ID
String[] orderIdArray = orderIdsStr.split(",");
for (String orderId : orderIdArray) {
orderId = orderId.trim();
// 去掉单引号或双引号
if ((orderId.startsWith("'") && orderId.endsWith("'")) ||
(orderId.startsWith("\"") && orderId.endsWith("\""))) {
orderId = orderId.substring(1, orderId.length() - 1);
}
if (StringUtils.isNotEmpty(orderId)) {
orderIds.add(orderId);
}
}
}
} else if (params.get("orderId") != null) {
// 单个开票orderId字符串兼容旧版本
orderIds.add(params.get("orderId").toString());
}
if (orderIds.isEmpty()) {
return AppletControllerUtil.appletWarning("请选择要开票的订单");
}
// 调试日志输出解析后的订单ID列表
System.out.println("解析后的订单ID列表: " + orderIds);
// 4. 调用批量开票方法
return InvoiceUtil.createBatchInvoice(user.getId(), orderIds, params);
} catch (Exception e) {
return AppletControllerUtil.appletError("开具发票失败:" + e.getMessage());
}
}
/**
* 获取用户已保存的发票信息
*
* @param request HTTP请求对象
* @return 发票信息
*/
@GetMapping("/saved")
public AjaxResult getUserSavedInvoiceInfo(HttpServletRequest request) {
try {
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取用户发票信息列表
List<Map<String, Object>> invoiceInfoList = InvoiceUtil.getUserInvoiceInfoList(user.getId());
return AppletControllerUtil.appletSuccess(invoiceInfoList);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取发票信息失败:" + e.getMessage());
}
}
/**
* 获取发票统计数据
*
* @param request HTTP请求对象
* @return 统计数据
*/
@GetMapping("/statistics")
public AjaxResult getInvoiceStatistics(HttpServletRequest request) {
try {
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取统计数据
Map<String, Object> statistics = InvoiceUtil.getInvoiceStatistics(user.getId());
return AppletControllerUtil.appletSuccess(statistics);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取发票统计数据失败:" + e.getMessage());
}
}
/**
* 保存或更新用户发票信息
*
* @param params 发票信息参数
* @param request HTTP请求对象
* @return 保存结果
*/
@PostMapping("/info")
public AjaxResult saveOrUpdateUserInvoiceInfo(@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.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 验证必要参数
if (params.get("invoiceTitle") == null || StringUtils.isEmpty(params.get("invoiceTitle").toString())) {
return AppletControllerUtil.appletWarning("发票抬头不能为空");
}
if (params.get("category") == null) {
return AppletControllerUtil.appletWarning("发票类别不能为空");
}
Integer category = Integer.valueOf(params.get("category").toString());
String invoiceTitle = params.get("invoiceTitle").toString();
// 4. 根据类别验证参数
if (category == 0) {
// 个人发票验证
if (invoiceTitle.length() > 50) {
return AppletControllerUtil.appletWarning("个人发票抬头长度不能超过50个字符");
}
// 检查是否填写了企业字段
if (StringUtils.isNotEmpty((String) params.get("taxNumber"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写纳税人识别号");
}
if (StringUtils.isNotEmpty((String) params.get("address"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写单位地址");
}
if (StringUtils.isNotEmpty((String) params.get("phone"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写联系电话");
}
if (StringUtils.isNotEmpty((String) params.get("bankName"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写开户银行");
}
if (StringUtils.isNotEmpty((String) params.get("bankAccount"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写银行账号");
}
if (StringUtils.isNotEmpty((String) params.get("wechat"))) {
return AppletControllerUtil.appletWarning("个人发票不能填写微信号");
}
} else if (category == 1) {
// 企业发票验证
if (StringUtils.isEmpty((String) params.get("taxNumber"))) {
return AppletControllerUtil.appletWarning("企业发票必须填写纳税人识别号");
}
if (StringUtils.isEmpty((String) params.get("address"))) {
return AppletControllerUtil.appletWarning("企业发票必须填写单位地址");
}
if (StringUtils.isEmpty((String) params.get("phone"))) {
return AppletControllerUtil.appletWarning("企业发票必须填写联系电话");
}
if (StringUtils.isEmpty((String) params.get("bankName"))) {
return AppletControllerUtil.appletWarning("企业发票必须填写开户银行");
}
if (StringUtils.isEmpty((String) params.get("bankAccount"))) {
return AppletControllerUtil.appletWarning("企业发票必须填写银行账号");
}
} else {
return AppletControllerUtil.appletWarning("发票类别参数错误");
}
// 5. 保存发票信息简化实现
return AppletControllerUtil.appletSuccess("发票信息保存成功");
} catch (Exception e) {
return AppletControllerUtil.appletError("保存发票信息失败:" + e.getMessage());
}
}
/**
* 获取用户发票信息列表
*
* @param request HTTP请求对象
* @return 发票信息列表
*/
@GetMapping("/list")
public AjaxResult getUserInvoiceInfoList(HttpServletRequest request) {
try {
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 获取用户发票信息列表
List<Map<String, Object>> invoiceInfoList = InvoiceUtil.getUserInvoiceInfoList(user.getId());
return AppletControllerUtil.appletSuccess(invoiceInfoList);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取发票信息列表失败:" + e.getMessage());
}
}
/**
* 删除用户发票信息
*
* @param id 发票信息ID
* @param request HTTP请求对象
* @return 删除结果
*/
@DeleteMapping("/info/{id}")
public AjaxResult deleteUserInvoiceInfo(@PathVariable("id") Integer id, HttpServletRequest request) {
try {
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 删除发票信息简化实现
return AppletControllerUtil.appletSuccess("发票信息删除成功");
} catch (Exception e) {
return AppletControllerUtil.appletError("删除发票信息失败:" + e.getMessage());
}
}
}

View File

@ -0,0 +1,848 @@
package com.ruoyi.system.controller;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.system.ControllerUtil.*;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 会员管理控制器
*
* 提供会员相关的API接口包括
* 1. 会员充值功能
* 2. 充值记录管理
* 3. 消费记录管理
* 4. 会员权益管理
* 5. 订单评价功能
* 6. 师傅报价功能
* 7. 拼团支付功能
* 8. 次卡管理功能
* 9. 余额支付功能
*
* @author Mr. Zhang Pan
* @version 1.0
* @date 2025-01-26
*/
@RestController
@RequestMapping("/api")
public class AppleMemberController extends BaseController {
// ==================== 依赖注入 ====================
@Autowired
private IUsersService usersService;
@Autowired
private IUserMemberRechargeLogService userMemberRechargeLogService;
@Autowired
private IUserMemberRechargeProgramService userMemberRechargeProgramService;
@Autowired
private ISiteConfigService siteConfigService;
@Autowired
private IUserMemnerConsumptionLogService userMemnerConsumptionLogService;
@Autowired
private IOrderService orderService;
@Autowired
private IOrderCommentService orderCommentService;
@Autowired
private IOrderLogService orderLogService;
@Autowired
private IServiceCateService serviceCateService;
@Autowired
private IUserSecondaryCardService userSecondaryCardService;
@Autowired
private IServiceGoodsService serviceGoodsService;
@Autowired
private IUserDemandQuotationService userDemandQuotationService;
@Autowired
private IUserGroupBuyingService userGroupBuyingService;
@Autowired
private IUserBenefitPointsService userBenefitPointsService;
@Autowired
private WechatPayUtil wechatPayUtil;
// ==================== 会员充值相关接口 ====================
/**
* 会员充值支付接口
*
* 支持两种充值方式
* 1. 通过充值套餐ID充值优先级更高
* 2. 通过自定义金额充值
*
* 业务逻辑
* - 如果id和money都有值优先使用id充值套餐
* - 如果只有money有值使用自定义金额充值
* - 支持充值优惠配置从系统配置中读取
* - 生成充值记录并调用微信支付
*
* @param request HTTP请求对象需要包含token
* @return 支付结果包含prepayId等微信支付参数
*/
@PostMapping("/member/recharge/pay")
public AjaxResult memberRechargePay(HttpServletRequest request) {
try {
// 1. 验证用户登录状态
Map<String, Object> params = new HashMap<>();
params.put("money", 300); // 测试用固定金额实际应从请求参数获取
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. 参数验证逻辑id和money必须有一个有值如果都有值则优先使用id
Object idObj = params.get("id");
Object moneyObj = params.get("money");
boolean idEmpty = (idObj == null || idObj.toString().trim().isEmpty());
boolean moneyEmpty = (moneyObj == null || moneyObj.toString().trim().isEmpty());
if (idEmpty && moneyEmpty) {
return AppletControllerUtil.appletWarning("参数不能为空,类目和金额必须有一个有值");
}
// 如果id和money都有值优先走id逻辑money置空
if (!idEmpty && !moneyEmpty) {
moneyObj = null;
}
// 4. 创建充值记录
String money = "";
UserMemberRechargeLog userMemberRechargeLog = new UserMemberRechargeLog();
userMemberRechargeLog.setUid(Math.toIntExact(user.getId()));
userMemberRechargeLog.setOrderid(GenerateCustomCode.generCreateOrder("DYZ"));
userMemberRechargeLog.setPaytype(0); // 0=微信支付
userMemberRechargeLog.setPaytime(new Date());
if (!idEmpty) {
// 5a. 通过充值套餐ID充值
UserMemberRechargeProgram userMemberRechargeProgram = userMemberRechargeProgramService
.selectUserMemberRechargeProgramById(Integer.valueOf(idObj.toString()));
if (userMemberRechargeProgram != null) {
userMemberRechargeLog.setInmoney(userMemberRechargeProgram.getMoney()); // 应付金额
userMemberRechargeLog.setComemoney(userMemberRechargeProgram.getDiscount()); // 实际到账金额
userMemberRechargeLog.setReamk("购买" + userMemberRechargeProgram.getRechargename()
+ "应付" + userMemberRechargeProgram.getMoney() + "元,应到"
+ userMemberRechargeProgram.getDiscount() + "");
userMemberRechargeLog.setProid(userMemberRechargeProgram.getId());
money = userMemberRechargeProgram.getMoney().toString();
// type大于0表示会员包年充值需要特殊处理
if (userMemberRechargeProgram.getType() > 0) {
userMemberRechargeLog.setIsmember(1); // 会员充值
} else {
userMemberRechargeLog.setIsmember(2); // 普通充值
}
}
} else if (!moneyEmpty) {
// 5b. 通过自定义金额充值
money = moneyObj.toString();
BigDecimal rechargeAmount = new BigDecimal(money);
BigDecimal actualAmount = rechargeAmount; // 默认实际到账金额等于充值金额
try {
// 查询充值优惠配置
SiteConfig siteConfig = siteConfigService.selectSiteConfigByName("config_one");
if (siteConfig != null && siteConfig.getValue() != null) {
JSONObject configJson = JSONObject.parseObject(siteConfig.getValue());
if (configJson.containsKey("recharge_discount")) {
// 获取充值优惠率百分比
BigDecimal discountRate = configJson.getBigDecimal("recharge_discount");
if (discountRate != null && discountRate.compareTo(BigDecimal.ZERO) > 0) {
// 计算实际到账金额充值金额 * (1 + 优惠率/100)
BigDecimal discountMultiplier = BigDecimal.ONE.add(
discountRate.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP));
actualAmount = rechargeAmount.multiply(discountMultiplier)
.setScale(2, RoundingMode.HALF_UP);
}
}
}
} catch (Exception e) {
System.err.println("获取充值优惠配置失败:" + e.getMessage());
// 配置获取失败时使用原金额不影响充值流程
}
userMemberRechargeLog.setInmoney(rechargeAmount); // 应付金额
userMemberRechargeLog.setComemoney(actualAmount); // 实际到账金额
userMemberRechargeLog.setIsmember(2); // 普通充值
// 更新备注信息显示优惠详情
if (actualAmount.compareTo(rechargeAmount) > 0) {
BigDecimal bonusAmount = actualAmount.subtract(rechargeAmount);
userMemberRechargeLog.setReamk("会员现金充值" + money + "元,实际到账" + actualAmount
+ "元(含优惠" + bonusAmount + "元)");
} else {
userMemberRechargeLog.setReamk("会员现金充值" + money + "");
}
}
// 6. 保存充值记录并调用微信支付
if (userMemberRechargeLogService.insertUserMemberRechargeLog(userMemberRechargeLog) > 0) {
// 调用微信支付测试环境使用0.01元
Map<String, Object> payResult = wechatPayUtil.createBatchOrderAndPay(
user.getOpenid(),
userMemberRechargeLog.getId().toString(),
new BigDecimal("0.01"), // 测试金额
1,
wechatPayUtil.PAY_FH + "api/recharge/pay/notify");
if (payResult != null && Boolean.TRUE.equals(payResult.get("success"))) {
// 构建支付响应数据
Map<String, Object> responseData = new HashMap<>();
responseData.put("mainOrderId", userMemberRechargeLog.getId().toString());
responseData.put("totalAmount", money);
responseData.put("prepayId", payResult.get("prepayId"));
// 合并所有支付参数
responseData.putAll(payResult);
return AppletControllerUtil.appletSuccess(responseData);
} else {
String errorMsg = payResult != null ? (String) payResult.get("message") : "微信支付下单失败";
return AppletControllerUtil.appletWarning("支付下单失败:" + errorMsg);
}
}
return AppletControllerUtil.appletWarning("支付失败");
} catch (Exception e) {
System.err.println("会员充值支付异常:" + e.getMessage());
return AppletControllerUtil.appletError("充值支付失败:" + e.getMessage());
}
}
/**
* 获取充值类目列表
*
* 查询状态为0启用且类型为0普通充值的充值套餐
* 用于用户选择充值项目
*
* @return 充值类目列表
*/
@GetMapping("/member/recharge/catalogue")
public AjaxResult getRechargeCatalogue() {
try {
UserMemberRechargeProgram query = new UserMemberRechargeProgram();
query.setStatus(0); // 0=启用状态
query.setType(0); // 0=普通充值类型
List<UserMemberRechargeProgram> list = userMemberRechargeProgramService
.selectUserMemberRechargeProgramList(query);
return AppletControllerUtil.appletSuccess(list);
} catch (Exception e) {
return AppletControllerUtil.appletError("获取充值类目失败:" + e.getMessage());
}
}
/**
* 获取包年充值项目列表
*
* 根据类型ID查询对应的包年充值项目
* 用于会员包年充值功能
*
* @param id 充值类型ID
* @return 包年充值项目列表
*/
@GetMapping("/member/recharge/catal/{id}")
public AjaxResult getRechargeCatalyear(@PathVariable("id") int id) {
try {
UserMemberRechargeProgram query = new UserMemberRechargeProgram();
query.setStatus(0); // 0=启用状态
query.setType(id); // 指定类型
List<UserMemberRechargeProgram> list = userMemberRechargeProgramService
.selectUserMemberRechargeProgramList(query);
if (!list.isEmpty()) {
return AppletControllerUtil.appletSuccess(list);
} else {
return AppletControllerUtil.appletWarning("暂无数据");
}
} catch (Exception e) {
return AppletControllerUtil.appletError("获取充值类目失败:" + e.getMessage());
}
}
// ==================== 余额支付相关接口 ====================
/**
* 余额支付测试接口
*
* 测试用户余额支付功能
* 模拟购买99.99元商品的余额支付流程
*
* @param request HTTP请求对象需要包含token
* @return 支付结果
*/
@GetMapping("/balance/payment")
public AjaxResult apibalancepayment(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. 调用余额支付工具类
Map<String, Object> rmap = BalancePayUtil.processBalancePayment(
user.getId(),
new BigDecimal("99.99"),
"购买一般商品单价99.99");
return AppletControllerUtil.appletSuccess(rmap.get("message"));
} catch (Exception e) {
System.err.println("余额支付异常:" + e.getMessage());
return AppletControllerUtil.appletError("余额支付失败:" + e.getMessage());
}
}
// ==================== 记录查询相关接口 ====================
/**
* 获取用户充值记录列表
*
* 查询当前用户的所有充值记录
* 按时间倒序排列
*
* @param request HTTP请求对象需要包含token
* @return 用户充值记录列表
*/
@GetMapping("/member/recharge/log")
public AjaxResult getRechargelog(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. 查询充值记录
UserMemberRechargeLog query = new UserMemberRechargeLog();
query.setUid(Math.toIntExact(user.getId()));
List<UserMemberRechargeLog> list = userMemberRechargeLogService
.selectUserMemberRechargeLogList(query);
if (!list.isEmpty()) {
return AppletControllerUtil.appletSuccess(list);
} else {
return AppletControllerUtil.appletWarning("暂无数据");
}
} catch (Exception e) {
return AppletControllerUtil.appletError("获取数据失败:" + e.getMessage());
}
}
/**
* 获取用户消费记录
*
* 查询当前用户的消费记录
* 注意这里只返回第一条记录可能是设计问题建议返回完整列表
*
* @param request HTTP请求对象需要包含token
* @return 用户消费记录
*/
@GetMapping("/member/consumption/log")
public AjaxResult getconsumptionlog(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. 查询消费记录
UserMemnerConsumptionLog query = new UserMemnerConsumptionLog();
query.setUid(Math.toIntExact(user.getId()));
List<UserMemnerConsumptionLog> list = userMemnerConsumptionLogService
.selectUserMemnerConsumptionLogList(query);
if (!list.isEmpty()) {
// 注意这里只返回第一条记录可能需要根据业务需求调整
return AppletControllerUtil.appletSuccess(list.getFirst());
} else {
return AppletControllerUtil.appletWarning("暂无数据");
}
} catch (Exception e) {
return AppletControllerUtil.appletError("获取消费记录失败:" + e.getMessage());
}
}
// ==================== 订单评价相关接口 ====================
/**
* 服务订单评价接口
*
* 用户对已完成的服务订单进行评价
* 包括评分评价内容图片标签等
*
* 业务逻辑
* 1. 验证用户登录状态
* 2. 检查订单是否存在且属于当前用户
* 3. 检查是否已经评价过防止重复评价
* 4. 保存评价信息
* 5. 添加订单日志
* 6. 更新订单状态为已完成
*
* @param params 评价参数order_id, content, num, images, labels
* @param request HTTP请求对象需要包含token
* @return 评价结果
*/
@PostMapping("/service/order/comment")
public AjaxResult serviceOrderComment(@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.containsKey("order_id") || !params.containsKey("content") || !params.containsKey("num")) {
return AppletControllerUtil.appletWarning("参数错误");
}
String orderId = params.get("order_id").toString();
String content = params.get("content").toString();
Integer num = Integer.parseInt(params.get("num").toString());
// 4. 获取订单信息并验证
Order order = orderService.selectOrderByOrderId(orderId);
if (order == null) {
return AppletControllerUtil.appletWarning("订单不存在");
}
// 5. 检查是否已经评价过
int count = orderCommentService.selectCountOrderCommentByOid(order.getId());
if (count > 0) {
return AppletControllerUtil.appletWarning("请勿重复提交");
}
// 6. 计算评分类型
Integer numType;
if (num == 1) {
numType = 3; // 差评
} else if (num == 2 || num == 3) {
numType = 2; // 中评
} else {
numType = 1; // 好评
}
// 7. 构建评价数据
OrderComment comment = new OrderComment();
comment.setOid(order.getId());
comment.setOrderId(orderId);
comment.setProductId(order.getProductId());
comment.setContent(content);
comment.setNum(Long.valueOf(num));
comment.setNumType(Long.valueOf(numType));
comment.setUid(user.getId());
comment.setWorkerId(order.getWorkerId());
// 8. 处理图片附件
if (params.containsKey("images") && params.get("images") != null) {
String images = JSON.toJSONString(params.get("images"));
comment.setImages(images);
}
// 9. 处理评价标签
if (params.containsKey("labels") && params.get("labels") != null) {
String labels = JSON.toJSONString(params.get("labels"));
comment.setLabels(labels);
}
// 10. 保存评价并更新订单状态
// 保存评价
orderCommentService.insertOrderComment(comment);
// 添加订单日志
OrderLog orderLog = new OrderLog();
orderLog.setOid(order.getId());
orderLog.setOrderId(orderId);
orderLog.setTitle("订单评价");
orderLog.setType(BigDecimal.valueOf(8)); // 8=评价类型
Map<String, Object> logContent = new HashMap<>();
logContent.put("text", content);
logContent.put("image", params.get("images"));
logContent.put("num", num);
orderLog.setContent(JSON.toJSONString(logContent));
orderLogService.insertOrderLog(orderLog);
// 更新订单状态
order.setStatus(4L); // 4=完成状态
order.setIsComment(1); // 1=已评价
orderService.updateOrder(order);
return AjaxResult.success("评价提交成功");
} catch (Exception e) {
System.err.println("服务订单评价异常:" + e.getMessage());
return AjaxResult.error("评价提交失败:" + e.getMessage());
}
}
// ==================== 用户验证相关接口 ====================
/**
* 检查用户是否使用默认头像和昵称
*
* 验证用户是否还在使用系统默认的头像和昵称
* 如果是默认的提示用户修改
*
* @param request HTTP请求对象需要包含token
* @return 验证结果
*/
@GetMapping("/user/check/default")
public AjaxResult checkUserDefault(HttpServletRequest request) {
try {
// 1. 验证用户登录状态
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AppletControllerUtil.appletUnauthorized();
}
// 2. 获取用户信息
Users user = (Users) userValidation.get("user");
if (user == null) {
return AppletControllerUtil.appletWarning("用户信息获取失败");
}
// 3. 验证图像和昵称是否为系统默认
String defaultAvatar = "https://img.huafurenjia.cn/default/user_avatar.jpeg";
String defaultName = "微信用户";
if (defaultAvatar.equals(user.getAvatar()) && defaultName.equals(user.getName())) {
return AppletControllerUtil.appletWarning("请修改您的图像和昵称");
}
return AppletControllerUtil.appletSuccess("校验通过");
} catch (Exception e) {
System.err.println("验证用户图像和昵称异常:" + e.getMessage());
return AppletControllerUtil.appletError("验证失败:" + e.getMessage());
}
}
// ==================== 分类和次卡相关接口 ====================
/**
* 获取二级分类列表
*
* 查询所有的服务分类信息
* 用于前端分类展示
*
* @param request HTTP请求对象
* @return 分类列表
*/
@GetMapping("/secondary/classification")
public AjaxResult classification(HttpServletRequest request) {
try {
List<ServiceCate> list = serviceCateService.selectServiceCateCiKaList();
return AppletControllerUtil.appletSuccess(list);
} catch (Exception e) {
System.err.println("获取二级分类异常:" + e.getMessage());
return AppletControllerUtil.appletError("获取分类失败:" + e.getMessage());
}
}
/**
* 获取次卡列表支持分页
*
* 查询用户次卡列表支持按类型筛选和分页
* 每个次卡会包含对应的服务商品详情
*
* @param params 查询参数page, limit, type
* @param request HTTP请求对象
* @return 次卡列表分页格式
*/
@PostMapping("/secondary/card/list")
public AjaxResult getSecondaryCardList(@RequestBody Map<String, Object> params, HttpServletRequest request) {
try {
// 1. 获取并验证分页参数
int page = params.get("page") != null ? Integer.parseInt(params.get("page").toString()) : 1;
int limit = params.get("limit") != null ? Integer.parseInt(params.get("limit").toString()) : 15;
Map<String, Object> pageValidation = PageUtil.validatePageParams(page, limit);
if (!(Boolean) pageValidation.get("valid")) {
return AppletControllerUtil.appletWarning((String) pageValidation.get("message"));
}
// 2. 获取type参数
Long type = params.get("type") != null ? Long.parseLong(params.get("type").toString()) : null;
// 3. 创建查询对象
UserSecondaryCard queryParams = new UserSecondaryCard();
queryParams.setStatus(1L); // 只查询状态为1的数据
if (type != null) {
queryParams.setType(type);
}
// 4. 设置分页参数
PageHelper.startPage(page, limit);
// 5. 执行查询
List<UserSecondaryCard> list = userSecondaryCardService.selectUserSecondaryCardList(queryParams);
// 6. 为每个次卡填充服务商品详情
for (UserSecondaryCard card : list) {
List<String> idsList = com.alibaba.fastjson2.JSONArray.parseArray(card.getGoodsids(), String.class);
card.setServiceDetail(serviceGoodsService.selectServiceGoodsfrocikaList(idsList));
}
// 7. 获取分页信息并构建响应
TableDataInfo tableDataInfo = getDataTable(list);
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());
}
}
// ==================== 师傅报价相关接口 ====================
/**
* 师傅报价接口
*
* 师傅对用户发布的需求订单进行报价
* 支持新增报价和更新已有报价
*
* 业务逻辑
* 1. 验证师傅登录状态
* 2. 验证订单是否存在
* 3. 检查是否已经报过价
* 4. 如果已报价则更新否则新增报价记录
*
* @param params 报价参数orderid, money
* @param request HTTP请求对象需要包含token
* @return 报价结果
*/
@PostMapping("/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());
}
}
// ==================== 拼团支付相关接口 ====================
// ==================== 用户权益相关接口 ====================
/**
* 查询用户服务金/消费金日志支持分页
*
* 查询用户的服务金或消费金变动日志
* 支持分页查询按时间倒序排列
*
* @param type 日志类型1=服务金2=消费金
* @param limit 每页条数默认10
* @param page 页码默认1
* @param request HTTP请求对象需要包含token
* @return 权益日志列表分页格式
*/
@GetMapping("/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());
}
}
/**
* 小程序用户余额日志查询接口
*
* 查询用户的余额变动日志
* 调用工具类方法统一处理
*
* @param request HTTP请求对象需要包含token
* @param params 查询参数
* @return 余额日志列表
*/
@PostMapping("/user/balance/logs")
public AjaxResult getBalanceLogList(HttpServletRequest request, @RequestBody Map<String, Object> params) {
try {
// 从请求头获取token
String token = request.getHeader("token");
// 调用工具类方法统一处理
return AppletControllerUtil.getUserBalanceLogList(params, token, usersService, userMemnerConsumptionLogService);
} catch (Exception e) {
System.err.println("查询用户余额日志异常:" + e.getMessage());
return AppletControllerUtil.appletError("查询余额日志失败:" + e.getMessage());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,164 +0,0 @@
package com.ruoyi.system.controller;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.ControllerUtil.*;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 游标工具控制器
*
* @author Mr. Zhang Pan
* @version 1.0
* @date 2025-01-10
*/
@RestController
public class CursorUtil extends BaseController {
@Autowired
private IServiceGoodsService serviceGoodsService;
@Autowired
private IGoodsOrderCursorService goodsOrderCursorService;
@Autowired
private IUsersService usersService;
@Autowired
private IUserAddressService userAddressService;
@Autowired
private IGoodsOrderService goodsOrderService;
@Autowired
private WechatPayUtil wechatPayUtil;
@Autowired
private IOrderService orderService;
@Autowired
private IOrderLogService orderLogService;
@Autowired
private IOrderCommentService orderCommentService;
@Autowired
private IUsersInvoiceInfoService usersInvoiceInfoService;
/**
* 小程序接口维护用户发票信息新增/修改
* POST /api/user/invoice/info
* @param params 发票信息参数
* @param request 请求对象
* @return AjaxResult
*/
@PostMapping("/api/user/invoicedata/info")
public AjaxResult saveOrUpdateInvoiceInfo(@RequestBody Map<String, Object> params, HttpServletRequest request) {
// 1. 校验用户登录
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AjaxResult.error("用户未登录或token无效");
}
Users user = (Users) userValidation.get("user");
if (user == null) {
return AjaxResult.error("用户信息获取失败");
}
// 2. 构建发票对象
UsersInvoiceInfo invoiceInfo = new UsersInvoiceInfo();
invoiceInfo.setId(params.get("id") != null ? Integer.valueOf(params.get("id").toString()) : null);
invoiceInfo.setUid(user.getId() != null ? user.getId().intValue() : null);
invoiceInfo.setInvoiceTitle((String) params.get("invoiceTitle"));
invoiceInfo.setTaxNumber((String) params.get("taxNumber"));
invoiceInfo.setBankName((String) params.get("bankName"));
invoiceInfo.setBankAccount((String) params.get("bankAccount"));
invoiceInfo.setAddress((String) params.get("address"));
invoiceInfo.setPhone((String) params.get("phone"));
invoiceInfo.setEmail((String) params.get("email"));
invoiceInfo.setWechat((String) params.get("wechat"));
invoiceInfo.setType(params.get("type") != null ? Integer.valueOf(params.get("type").toString()) : 1);
invoiceInfo.setCategory(params.get("category") != null ? Integer.valueOf(params.get("category").toString()) : 1);
// 3. 保存或更新
try {
if (invoiceInfo.getId() != null) {
usersInvoiceInfoService.updateUsersInvoiceInfo(invoiceInfo);
} else {
usersInvoiceInfoService.insertUsersInvoiceInfo(invoiceInfo);
}
return AjaxResult.success("发票信息保存成功");
} catch (Exception e) {
return AjaxResult.error("发票信息保存失败: " + e.getMessage());
}
}
/**
* 小程序接口查询当前用户所有发票信息
* GET /api/user/invoice/list
* @param request 请求对象
* @return AjaxResult
*/
@GetMapping("/api/user/invoicedata/list")
public AjaxResult getInvoicelist(HttpServletRequest request) {
// 1. 校验用户登录
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AjaxResult.error("用户未登录或token无效");
}
Users user = (Users) userValidation.get("user");
if (user == null) {
return AjaxResult.error("用户信息获取失败");
}
UsersInvoiceInfo invoiceInfo = new UsersInvoiceInfo();
invoiceInfo.setUid(user.getId() != null ? user.getId().intValue() : null);
// 2. 查询所有发票信息
List<UsersInvoiceInfo> list = usersInvoiceInfoService.selectUsersInvoiceInfoList(invoiceInfo);
return AjaxResult.success(list);
}
/**
* 小程序接口删除用户单个发票信息
* DELETE /api/user/invoice/delete/{id}
* @param id 发票ID
* @param request 请求对象
* @return AjaxResult
*/
@DeleteMapping("/api/user/invoicedata/delete/{id}")
public AjaxResult deleteInvoiceInfo(@PathVariable Integer id, HttpServletRequest request) {
// 1. 校验用户登录
String token = request.getHeader("token");
Map<String, Object> userValidation = AppletLoginUtil.validateUserToken(token, usersService);
if (!(Boolean) userValidation.get("valid")) {
return AjaxResult.error("用户未登录或token无效");
}
Users user = (Users) userValidation.get("user");
if (user == null) {
return AjaxResult.error("用户信息获取失败");
}
// 2. 删除发票需校验归属
try {
int result = usersInvoiceInfoService.deleteUsersInvoiceInfoById(id);
if (result > 0) {
return AjaxResult.success("删除成功");
} else {
return AjaxResult.error("删除失败或无权限");
}
} catch (Exception e) {
return AjaxResult.error("删除异常: " + e.getMessage());
}
}
}

View File

@ -300,6 +300,18 @@ public class PayNotifyController extends BaseController {
userMemberRechargeLog.setPaytype(1);
userMemberRechargeLog.setTransactionId(transactionId);
userMemberRechargeLogService.updateUserMemberRechargeLog(userMemberRechargeLog);
//成功之后就要给大哥加流水确保大哥的账户流水里面有这条充值的记录如果购买了会员下一步扣除就可以
UserMemnerConsumptionLog newuserMemnerConsumptionLog = new UserMemnerConsumptionLog();
newuserMemnerConsumptionLog.setUid(Math.toIntExact(users.getId()));
newuserMemnerConsumptionLog.setConsumptiontype(3);
newuserMemnerConsumptionLog.setConsumptiontime(new Date());
newuserMemnerConsumptionLog.setConsumptionmoney(userMemberRechargeLog.getComemoney());
newuserMemnerConsumptionLog.setReamk("用户充值"+userMemberRechargeLog.getInmoney()+"实际到账"+userMemberRechargeLog.getComemoney());
newuserMemnerConsumptionLog.setBeformoney(users.getBalance());
newuserMemnerConsumptionLog.setAftermoney(users.getBalance().add(userMemberRechargeLog.getComemoney()));
newuserMemnerConsumptionLog.setNowmoney(users.getBalance().add(userMemberRechargeLog.getComemoney()));
newuserMemnerConsumptionLog.setType(1);
userMemnerConsumptionLogService.insertUserMemnerConsumptionLog(newuserMemnerConsumptionLog);
users.setBalance(users.getBalance().add(userMemberRechargeLog.getComemoney()));
usersService.updateUsers(users);
WXsendMsgUtil.sendUserPayMoney(users.getOpenid(),"充值成功",totalFee,"1","充值成功");
@ -310,10 +322,11 @@ public class PayNotifyController extends BaseController {
userMemnerConsumptionLog.setConsumptiontype(2);
userMemnerConsumptionLog.setConsumptiontime(new Date());
userMemnerConsumptionLog.setConsumptionmoney(userMemberRechargeLog.getComemoney());
userMemnerConsumptionLog.setReamk("会员包年充值");
userMemnerConsumptionLog.setReamk("会员包年扣减");
userMemnerConsumptionLog.setBeformoney(users.getBalance());
userMemnerConsumptionLog.setAftermoney(users.getBalance().subtract(userMemberRechargeLog.getComemoney()));
userMemnerConsumptionLog.setNowmoney(users.getBalance().subtract(userMemberRechargeLog.getComemoney()));
userMemnerConsumptionLog.setType(2);
userMemnerConsumptionLogService.insertUserMemnerConsumptionLog(userMemnerConsumptionLog);
users.setBalance(users.getBalance().subtract(userMemberRechargeLog.getComemoney()));
users.setIsmember(1);

View File

@ -6,22 +6,30 @@ import com.ruoyi.system.domain.Users;
import com.ruoyi.system.domain.UserMemnerConsumptionLog;
import com.ruoyi.system.service.IUserMemnerConsumptionLogService;
import com.ruoyi.system.service.IUsersService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 余额支付工具类
*
* @author
* @author ruoyi
* @date 2025-01-26
*/
public class BalancePayUtil {
private static final Logger logger = LoggerFactory.getLogger(BalancePayUtil.class);
private static final IUserMemnerConsumptionLogService userMemnerConsumptionLogService = SpringUtils.getBean(IUserMemnerConsumptionLogService.class);
private static final IUsersService usersService = SpringUtils.getBean(IUsersService.class);
/**
* 用户余额支付
* 用户余额支付原有方法保持不变
* @param user 用户对象
* @param amount 支付金额
* @return AjaxResult 支付结果
@ -57,14 +65,212 @@ public class BalancePayUtil {
log.setBeformoney(beforeBalance);
log.setAftermoney(user.getBalance());
log.setNowmoney(user.getBalance());
log.setType(2); // 2=支出
userMemnerConsumptionLogService.insertUserMemnerConsumptionLog(log);
// 推送支付成功消息
try {
WXsendMsgUtil.sendUserPayMoney(user.getOpenid(), "余额支付成功", amount.toString(), "1", "余额支付成功");
} catch (Exception e) {
// 推送失败不影响主流程
System.err.println("余额支付消息推送失败: " + e.getMessage());
logger.warn("余额支付消息推送失败: {}", e.getMessage());
}
return AjaxResult.success("余额支付成功");
}
/**
* 用户余额支付增强版
*
* @param userId 用户ID
* @param money 支付金额
* @param remark 支付备注
* @return Map<String, Object> 支付结果
*
* 返回结果说明
* - success: true/false 支付是否成功
* - message: 结果消息
* - insufficientAmount: 余额不足时还差多少钱仅在余额不足时返回
* - beforeBalance: 支付前余额
* - afterBalance: 支付后余额
* - consumptionLogId: 消费记录ID
*/
public static Map<String, Object> processBalancePayment(Long userId, BigDecimal money, String remark) {
Map<String, Object> result = new HashMap<>();
try {
// 1. 参数验证
if (userId == null || userId <= 0) {
result.put("success", false);
result.put("message", "用户ID无效");
return result;
}
if (money == null || money.compareTo(BigDecimal.ZERO) <= 0) {
result.put("success", false);
result.put("message", "支付金额必须大于0");
return result;
}
if (remark == null || remark.trim().isEmpty()) {
remark = "余额支付";
}
// 2. 获取用户信息
Users user = usersService.selectUsersById(userId);
if (user == null) {
result.put("success", false);
result.put("message", "用户不存在");
return result;
}
// 3. 检查用户余额
BigDecimal currentBalance = user.getBalance();
if (currentBalance == null) {
currentBalance = BigDecimal.ZERO;
user.setBalance(currentBalance);
}
result.put("beforeBalance", currentBalance);
// 4. 判断余额是否足够
if (currentBalance.compareTo(money) < 0) {
// 余额不足计算还差多少钱
BigDecimal insufficientAmount = money.subtract(currentBalance);
result.put("success", false);
result.put("message", "余额不足");
result.put("insufficientAmount", insufficientAmount);
result.put("currentBalance", currentBalance);
result.put("requiredAmount", money);
logger.info("用户{}余额支付失败,余额不足。当前余额:{},需要支付:{},还差:{}",
userId, currentBalance, money, insufficientAmount);
return result;
}
// 5. 执行余额扣减
BigDecimal afterBalance = currentBalance.subtract(money);
user.setBalance(afterBalance);
int updateResult = usersService.updateUsers(user);
if (updateResult <= 0) {
result.put("success", false);
result.put("message", "余额扣减失败,请重试");
return result;
}
// 6. 记录消费流水
UserMemnerConsumptionLog consumptionLog = new UserMemnerConsumptionLog();
consumptionLog.setUid(Math.toIntExact(userId));
consumptionLog.setConsumptiontype(1); // 1=余额支付消费
consumptionLog.setConsumptiontime(new Date());
consumptionLog.setConsumptionmoney(money);
consumptionLog.setReamk(remark);
consumptionLog.setBeformoney(currentBalance);
consumptionLog.setAftermoney(afterBalance);
consumptionLog.setNowmoney(afterBalance);
consumptionLog.setType(2); // 2=支出
int logResult = userMemnerConsumptionLogService.insertUserMemnerConsumptionLog(consumptionLog);
// 7. 构建成功结果
result.put("success", true);
result.put("message", "余额支付成功");
result.put("afterBalance", afterBalance);
result.put("paymentAmount", money);
result.put("remark", remark);
if (logResult > 0) {
result.put("consumptionLogId", consumptionLog.getId());
}
// 8. 发送支付成功通知可选
try {
if (user.getOpenid() != null && !user.getOpenid().trim().isEmpty()) {
WXsendMsgUtil.sendUserPayMoney(user.getOpenid(), "余额支付成功",
money.toString(), "1", remark);
}
} catch (Exception e) {
logger.warn("余额支付成功通知发送失败用户ID{},错误:{}", userId, e.getMessage());
}
logger.info("用户{}余额支付成功,支付金额:{},支付前余额:{},支付后余额:{},备注:{}",
userId, money, currentBalance, afterBalance, remark);
return result;
} catch (Exception e) {
logger.error("余额支付处理异常用户ID{},支付金额:{},备注:{}", userId, money, remark, e);
result.put("success", false);
result.put("message", "余额支付处理异常:" + e.getMessage());
return result;
}
}
/**
* 检查用户余额是否足够
*
* @param userId 用户ID
* @param requiredAmount 需要的金额
* @return Map<String, Object> 检查结果
*
* 返回结果说明
* - sufficient: true/false 余额是否足够
* - currentBalance: 当前余额
* - requiredAmount: 需要的金额
* - insufficientAmount: 不足的金额仅在余额不足时返回
*/
public static Map<String, Object> checkBalanceSufficiency(Long userId, BigDecimal requiredAmount) {
Map<String, Object> result = new HashMap<>();
try {
// 1. 参数验证
if (userId == null || userId <= 0) {
result.put("sufficient", false);
result.put("message", "用户ID无效");
return result;
}
if (requiredAmount == null || requiredAmount.compareTo(BigDecimal.ZERO) <= 0) {
result.put("sufficient", false);
result.put("message", "检查金额必须大于0");
return result;
}
// 2. 获取用户信息
Users user = usersService.selectUsersById(userId);
if (user == null) {
result.put("sufficient", false);
result.put("message", "用户不存在");
return result;
}
// 3. 检查余额
BigDecimal currentBalance = user.getBalance();
if (currentBalance == null) {
currentBalance = BigDecimal.ZERO;
}
result.put("currentBalance", currentBalance);
result.put("requiredAmount", requiredAmount);
if (currentBalance.compareTo(requiredAmount) >= 0) {
// 余额足够
result.put("sufficient", true);
result.put("message", "余额充足");
} else {
// 余额不足
BigDecimal insufficientAmount = requiredAmount.subtract(currentBalance);
result.put("sufficient", false);
result.put("message", "余额不足");
result.put("insufficientAmount", insufficientAmount);
}
return result;
} catch (Exception e) {
logger.error("检查余额充足性异常用户ID{},需要金额:{}", userId, requiredAmount, e);
result.put("sufficient", false);
result.put("message", "检查余额异常:" + e.getMessage());
return result;
}
}
}

View File

@ -0,0 +1,615 @@
package com.ruoyi.system.ControllerUtil;
import com.github.pagehelper.PageHelper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
/**
* 发票工具类
*
* @author ruoyi
* @date 2025-01-26
*/
public class InvoiceUtil {
private static final Logger logger = LoggerFactory.getLogger(InvoiceUtil.class);
private static final IUsersInvoiceInfoService usersInvoiceInfoService = SpringUtils.getBean(IUsersInvoiceInfoService.class);
private static final IOrderService orderService = SpringUtils.getBean(IOrderService.class);
private static final IGoodsOrderService goodsOrderService = SpringUtils.getBean(IGoodsOrderService.class);
private static final IUserMemberRechargeLogService userMemberRechargeLogService = SpringUtils.getBean(IUserMemberRechargeLogService.class);
private static final IUsersService usersService = SpringUtils.getBean(IUsersService.class);
/**
* 获取用户发票中心数据
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 发票中心数据
*/
public static Map<String, Object> getUserInvoiceCenter(Long userId, int page, int limit) {
try {
Map<String, Object> result = new HashMap<>();
// 1. 获取待开票订单列表
List<Map<String, Object>> pendingInvoices = getPendingInvoiceOrders(userId, page, limit);
// 2. 获取已开票列表
List<Map<String, Object>> completedInvoices = getCompletedInvoices(userId, page, limit);
// 3. 统计数据
Map<String, Object> statistics = getInvoiceStatistics(userId);
result.put("pendingInvoices", pendingInvoices);
result.put("completedInvoices", completedInvoices);
result.put("statistics", statistics);
return result;
} catch (Exception e) {
logger.error("获取用户发票中心数据失败", e);
return null;
}
}
/**
* 获取待开票订单列表
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 待开票订单列表
*/
public static List<Map<String, Object>> getPendingInvoiceOrders(Long userId, int page, int limit) {
try {
List<Map<String, Object>> pendingOrders = new ArrayList<>();
// 设置分页
PageHelper.startPage(page, limit);
// 1. 查询服务订单已完成且未开票
Order orderQuery = new Order();
orderQuery.setUid(userId);
orderQuery.setStatus(4L); // 已完成状态
List<Order> serviceOrders = orderService.selectOrderList(orderQuery);
for (Order order : serviceOrders) {
// 检查是否已开票
if (!isOrderInvoiced(order.getOrderId())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", order.getOrderId());
orderMap.put("orderType", "service"); // 服务订单
orderMap.put("orderTypeText", "服务订单");
orderMap.put("amount", order.getTotalPrice());
orderMap.put("title", order.getOrderId()+ "服务订单");
orderMap.put("createTime", order.getCreateTime());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// 2. 查询商品订单已完成且未开票
GoodsOrder goodsOrderQuery = new GoodsOrder();
goodsOrderQuery.setUid(userId);
goodsOrderQuery.setStatus(4L); // 已完成状态
List<GoodsOrder> goodsOrders = goodsOrderService.selectGoodsOrderList(goodsOrderQuery);
for (GoodsOrder goodsOrder : goodsOrders) {
// 检查是否已开票
if (!isOrderInvoiced(goodsOrder.getOrderId())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", goodsOrder.getOrderId());
orderMap.put("orderType", "goods"); // 商品订单
orderMap.put("orderTypeText", "商品订单");
orderMap.put("amount", goodsOrder.getTotalPrice());
orderMap.put("title", "商品订单");
orderMap.put("createTime", goodsOrder.getCreateTime());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// 3. 查询充值订单已完成且未开票
UserMemberRechargeLog rechargeQuery = new UserMemberRechargeLog();
rechargeQuery.setUid(Math.toIntExact(userId));
rechargeQuery.setPaytype(1); // 已完成状态
List<UserMemberRechargeLog> rechargeOrders = userMemberRechargeLogService.selectUserMemberRechargeLogList(rechargeQuery);
for (UserMemberRechargeLog recharge : rechargeOrders) {
// 检查是否已开票
if (!isOrderInvoiced(recharge.getOrderid())) {
Map<String, Object> orderMap = new HashMap<>();
orderMap.put("orderId", recharge.getOrderid());
orderMap.put("orderType", "recharge"); // 充值订单
orderMap.put("orderTypeText", "充值订单");
orderMap.put("amount", recharge.getInmoney());
orderMap.put("title", recharge.getReamk() != null ? recharge.getReamk() : "会员充值");
orderMap.put("createTime", recharge.getCreatedAt());
orderMap.put("canInvoice", true);
pendingOrders.add(orderMap);
}
}
// // 按创建时间倒序排列
// pendingOrders.sort((a, b) -> {
// Date timeA = (Date) a.get("createTime");
// Date timeB = (Date) b.get("createTime");
// return timeB.compareTo(timeA);
// });
return pendingOrders;
} catch (Exception e) {
logger.error("获取待开票订单列表失败", e);
return new ArrayList<>();
}
}
/**
* 获取已开票列表
*
* @param userId 用户ID
* @param page 页码
* @param limit 每页数量
* @return 已开票列表
*/
public static List<Map<String, Object>> getCompletedInvoices(Long userId, int page, int limit) {
try {
// 设置分页
PageHelper.startPage(page, limit);
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
List<Map<String, Object>> completedInvoices = new ArrayList<>();
for (UsersInvoiceInfo invoice : invoiceList) {
Map<String, Object> invoiceMap = new HashMap<>();
invoiceMap.put("id", invoice.getId());
invoiceMap.put("orderId", invoice.getOrderid());
invoiceMap.put("invoiceTitle", invoice.getInvoiceTitle());
invoiceMap.put("amount", invoice.getInvoicemoney());
invoiceMap.put("invoiceText", invoice.getInvoicetext());
invoiceMap.put("status", invoice.getStatus());
invoiceMap.put("statusText", getInvoiceStatusText(invoice.getStatus()));
invoiceMap.put("type", invoice.getType());
invoiceMap.put("typeText", getInvoiceTypeText(invoice.getType()));
invoiceMap.put("category", invoice.getCategory());
invoiceMap.put("categoryText", invoice.getCategory() == 0 ? "个人" : "企业");
invoiceMap.put("createTime", invoice.getCreatedAt());
invoiceMap.put("hasFile", StringUtils.isNotEmpty(invoice.getFiledata()));
invoiceMap.put("fileUrl", invoice.getFiledata());
completedInvoices.add(invoiceMap);
}
return completedInvoices;
} catch (Exception e) {
logger.error("获取已开票列表失败", e);
return new ArrayList<>();
}
}
/**
* 获取发票统计数据
*
* @param userId 用户ID
* @return 统计数据
*/
public static Map<String, Object> getInvoiceStatistics(Long userId) {
try {
Map<String, Object> statistics = new HashMap<>();
// 待开票数量
List<Map<String, Object>> pendingOrders = getPendingInvoiceOrders(userId, 1, 1000);
int pendingCount = pendingOrders.size();
// 已开票数量
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
int completedCount = invoiceList.size();
// 已开票总金额
BigDecimal totalAmount = invoiceList.stream()
.filter(invoice -> invoice.getInvoicemoney() != null)
.map(UsersInvoiceInfo::getInvoicemoney)
.reduce(BigDecimal.ZERO, BigDecimal::add);
statistics.put("pendingCount", pendingCount);
statistics.put("completedCount", completedCount);
statistics.put("totalAmount", totalAmount);
return statistics;
} catch (Exception e) {
logger.error("获取发票统计数据失败", e);
Map<String, Object> statistics = new HashMap<>();
statistics.put("pendingCount", 0);
statistics.put("completedCount", 0);
statistics.put("totalAmount", BigDecimal.ZERO);
return statistics;
}
}
/**
* 批量开具发票
*
* @param userId 用户ID
* @param orderIds 订单ID数组
* @param invoiceData 发票数据
* @return 开票结果
*/
public static AjaxResult createBatchInvoice(Long userId, List<String> orderIds, Map<String, Object> invoiceData) {
try {
// 1. 参数验证
if (orderIds == null || orderIds.isEmpty()) {
return AppletControllerUtil.appletWarning("请选择要开票的订单");
}
// 2. 验证发票数据
String validateResult = validateInvoiceData(invoiceData);
if (StringUtils.isNotEmpty(validateResult)) {
return AppletControllerUtil.appletWarning(validateResult);
}
List<String> successOrders = new ArrayList<>();
List<String> failedOrders = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
List<String> invoiceTexts = new ArrayList<>();
// 3. 逐个处理订单
for (String orderId : orderIds) {
try {
// 验证订单是否存在且属于当前用户
Map<String, Object> orderInfo = getOrderInfo(orderId, userId);
if (orderInfo == null) {
failedOrders.add(orderId + "(订单不存在或不属于当前用户)");
continue;
}
// 检查是否已开票
if (isOrderInvoiced(orderId)) {
failedOrders.add(orderId + "(已开过发票)");
continue;
}
// 累计金额和开票内容
BigDecimal orderAmount = (BigDecimal) orderInfo.get("amount");
if (orderAmount != null) {
totalAmount = totalAmount.add(orderAmount);
}
invoiceTexts.add((String) orderInfo.get("title"));
successOrders.add(orderId);
} catch (Exception e) {
failedOrders.add(orderId + "(处理异常: " + e.getMessage() + ")");
}
}
// 4. 如果没有可开票的订单
if (successOrders.isEmpty()) {
return AppletControllerUtil.appletWarning("没有可开票的订单:" + String.join(", ", failedOrders));
}
// 5. 创建发票记录
UsersInvoiceInfo invoice = new UsersInvoiceInfo();
invoice.setUid(userId.intValue());
invoice.setOrderid(String.join(",", successOrders)); // 多个订单ID用逗号分隔
invoice.setInvoiceTitle((String) invoiceData.get("invoiceTitle"));
invoice.setType((Integer) invoiceData.get("type"));
invoice.setCategory((Integer) invoiceData.get("category"));
invoice.setInvoicemoney(totalAmount); // 总金额
invoice.setInvoicetext(String.join("", invoiceTexts)); // 开票内容合并
invoice.setStatus(1); // 待开票
invoice.setCreatedAt(new Date());
// 根据发票类别设置不同字段
if ((Integer) invoiceData.get("category") == 0) {
// 个人发票
invoice.setEmail((String) invoiceData.get("email"));
// 清空企业字段
invoice.setTaxNumber(null);
invoice.setBankName(null);
invoice.setBankAccount(null);
invoice.setAddress(null);
invoice.setPhone(null);
invoice.setWechat(null);
} else {
// 企业发票
invoice.setTaxNumber((String) invoiceData.get("taxNumber"));
invoice.setAddress((String) invoiceData.get("address"));
invoice.setPhone((String) invoiceData.get("phone"));
invoice.setBankName((String) invoiceData.get("bankName"));
invoice.setBankAccount((String) invoiceData.get("bankAccount"));
invoice.setEmail((String) invoiceData.get("email"));
invoice.setWechat((String) invoiceData.get("wechat"));
}
// 6. 保存发票信息
int result = usersInvoiceInfoService.insertUsersInvoiceInfo(invoice);
if (result > 0) {
Map<String, Object> resultData = new HashMap<>();
resultData.put("successCount", successOrders.size());
resultData.put("successOrders", successOrders);
resultData.put("totalAmount", totalAmount);
resultData.put("invoiceId", invoice.getId());
if (!failedOrders.isEmpty()) {
resultData.put("failedCount", failedOrders.size());
resultData.put("failedOrders", failedOrders);
resultData.put("message", "发票申请提交成功,成功开票 " + successOrders.size() + " 个订单,失败 " + failedOrders.size() + " 个订单");
return AppletControllerUtil.appletSuccess(resultData);
} else {
resultData.put("message", "发票申请提交成功,共开票 " + successOrders.size() + " 个订单");
return AppletControllerUtil.appletSuccess(resultData);
}
} else {
return AppletControllerUtil.appletWarning("发票申请提交失败");
}
} catch (Exception e) {
logger.error("批量开具发票失败", e);
return AppletControllerUtil.appletError("批量开具发票失败:" + e.getMessage());
}
}
/**
* 开具发票单个订单兼容旧接口
*
* @param userId 用户ID
* @param orderId 订单ID
* @param invoiceData 发票数据
* @return 开票结果
*/
public static AjaxResult createInvoice(Long userId, String orderId, Map<String, Object> invoiceData) {
List<String> orderIds = new ArrayList<>();
orderIds.add(orderId);
return createBatchInvoice(userId, orderIds, invoiceData);
}
/**
* 获取订单信息
*
* @param orderId 订单ID
* @param userId 用户ID
* @return 订单信息
*/
private static Map<String, Object> getOrderInfo(String orderId, Long userId) {
try {
// 1. 查询服务订单
Order serviceOrder = orderService.selectOrderByOrderId(orderId);
if (serviceOrder != null && serviceOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", serviceOrder.getOrderId());
orderInfo.put("amount", serviceOrder.getTotalPrice());
orderInfo.put("title", serviceOrder.getOrderId()+ "服务订单");
orderInfo.put("type", "service");
return orderInfo;
}
// 2. 查询商品订单
GoodsOrder goodsOrder = goodsOrderService.selectGoodsOrderByorderId(orderId);
if (goodsOrder != null && goodsOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", goodsOrder.getOrderId());
orderInfo.put("amount", goodsOrder.getTotalPrice());
orderInfo.put("title", "商品订单");
orderInfo.put("type", "goods");
return orderInfo;
}
// 3. 查询充值订单
UserMemberRechargeLog rechargeOrder = userMemberRechargeLogService.selectUserMemberRechargeLogByorderid(orderId);
if (rechargeOrder != null && rechargeOrder.getUid().equals(userId)) {
Map<String, Object> orderInfo = new HashMap<>();
orderInfo.put("orderId", rechargeOrder.getOrderid());
orderInfo.put("amount", rechargeOrder.getInmoney());
orderInfo.put("title", rechargeOrder.getReamk() != null ? rechargeOrder.getReamk() : "会员充值");
orderInfo.put("type", "recharge");
return orderInfo;
}
return null;
} catch (Exception e) {
logger.error("获取订单信息失败", e);
return null;
}
}
/**
* 检查订单是否已开票
*
* @param orderId 订单ID
* @return 是否已开票
*/
private static boolean isOrderInvoiced(String orderId) {
try {
// 查询所有发票记录
UsersInvoiceInfo query = new UsersInvoiceInfo();
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
// 检查订单ID是否在任何发票记录中
for (UsersInvoiceInfo invoice : invoiceList) {
if (invoice.getOrderid() != null) {
// 支持单个订单ID或多个订单ID逗号分隔
String[] orderIds = invoice.getOrderid().split(",");
for (String id : orderIds) {
if (id.trim().equals(orderId)) {
return true;
}
}
}
}
return false;
} catch (Exception e) {
logger.error("检查订单开票状态失败", e);
return false;
}
}
/**
* 验证发票数据
*
* @param invoiceData 发票数据
* @return 验证结果为空表示验证通过
*/
private static String validateInvoiceData(Map<String, Object> invoiceData) {
// 基础字段验证
if (invoiceData.get("invoiceTitle") == null || StringUtils.isEmpty(invoiceData.get("invoiceTitle").toString())) {
return "发票抬头不能为空";
}
if (invoiceData.get("type") == null) {
return "发票类型不能为空";
}
if (invoiceData.get("category") == null) {
return "发票类别不能为空";
}
Integer category = (Integer) invoiceData.get("category");
String invoiceTitle = invoiceData.get("invoiceTitle").toString();
// 个人发票验证
if (category == 0) {
if (invoiceTitle.length() > 50) {
return "个人发票抬头长度不能超过50个字符";
}
// 检查是否填写了企业字段
if (StringUtils.isNotEmpty((String) invoiceData.get("taxNumber"))) {
return "个人发票不能填写纳税人识别号";
}
if (StringUtils.isNotEmpty((String) invoiceData.get("address"))) {
return "个人发票不能填写单位地址";
}
if (StringUtils.isNotEmpty((String) invoiceData.get("phone"))) {
return "个人发票不能填写联系电话";
}
if (StringUtils.isNotEmpty((String) invoiceData.get("wechat"))) {
return "个人发票不能填写微信号";
}
}
// 企业发票验证
else if (category == 1) {
if (invoiceTitle.length() > 100) {
return "企业发票抬头长度不能超过100个字符";
}
// 必填字段验证
if (StringUtils.isEmpty((String) invoiceData.get("taxNumber"))) {
return "企业发票的纳税人识别号不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("address"))) {
return "企业发票的单位地址不能为空";
}
if (StringUtils.isEmpty((String) invoiceData.get("phone"))) {
return "企业发票的联系电话不能为空";
}
// 格式验证
String taxNumber = (String) invoiceData.get("taxNumber");
if (!taxNumber.matches("^[A-Z0-9]{15,20}$")) {
return "纳税人识别号格式不正确";
}
String phone = (String) invoiceData.get("phone");
if (!phone.matches("^1[3-9]\\d{9}$|^0\\d{2,3}-?\\d{7,8}$|^400-?\\d{3}-?\\d{4}$")) {
return "联系电话格式不正确";
}
}
return null; // 验证通过
}
/**
* 获取发票状态文本
*
* @param status 状态值
* @return 状态文本
*/
private static String getInvoiceStatusText(Integer status) {
if (status == null) return "未知";
switch (status) {
case 1: return "待开票";
case 2: return "已开票";
case 3: return "已完成";
default: return "未知";
}
}
/**
* 获取发票类型文本
*
* @param type 类型值
* @return 类型文本
*/
private static String getInvoiceTypeText(Integer type) {
if (type == null) return "未知";
switch (type) {
case 1: return "增值税专用发票";
case 2: return "增值税普通发票";
case 3: return "电子发票";
default: return "未知";
}
}
/**
* 获取用户发票信息列表用于选择已保存的发票信息
*
* @param userId 用户ID
* @return 发票信息列表
*/
public static List<Map<String, Object>> getUserInvoiceInfoList(Long userId) {
try {
UsersInvoiceInfo query = new UsersInvoiceInfo();
query.setUid(userId.intValue());
List<UsersInvoiceInfo> invoiceList = usersInvoiceInfoService.selectUsersInvoiceInfoList(query);
// 去重只保留不同的发票抬头
Map<String, UsersInvoiceInfo> uniqueInvoices = new HashMap<>();
for (UsersInvoiceInfo invoice : invoiceList) {
String key = invoice.getCategory() + "_" + invoice.getInvoiceTitle();
if (!uniqueInvoices.containsKey(key)) {
uniqueInvoices.put(key, invoice);
}
}
List<Map<String, Object>> result = new ArrayList<>();
for (UsersInvoiceInfo invoice : uniqueInvoices.values()) {
Map<String, Object> invoiceMap = new HashMap<>();
invoiceMap.put("id", invoice.getId());
invoiceMap.put("invoiceTitle", invoice.getInvoiceTitle());
invoiceMap.put("category", invoice.getCategory());
invoiceMap.put("categoryText", invoice.getCategory() == 0 ? "个人" : "企业");
invoiceMap.put("taxNumber", invoice.getTaxNumber());
invoiceMap.put("address", invoice.getAddress());
invoiceMap.put("phone", invoice.getPhone());
invoiceMap.put("bankName", invoice.getBankName());
invoiceMap.put("bankAccount", invoice.getBankAccount());
invoiceMap.put("email", invoice.getEmail());
invoiceMap.put("wechat", invoice.getWechat());
result.add(invoiceMap);
}
return result;
} catch (Exception e) {
logger.error("获取用户发票信息列表失败", e);
return new ArrayList<>();
}
}
}

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.config.QiniuConfig;
import com.ruoyi.system.config.WechatConfig;
import org.apache.xmlbeans.impl.xb.xsdschema.Public;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@ -78,7 +79,9 @@ public class WechatPayUtil {
private static final String WECHAT_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; // 订单查询
private static final String WECHAT_REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
private static final String WECHAT_TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款
public static final String PAY_FH = "https://4b29b18c.r3.cpolar.top/";
/**
* 其他配置常量
*/
@ -138,7 +141,7 @@ public class WechatPayUtil {
if (notifyUrl == null || notifyUrl.trim().isEmpty()) {
return failResult("回调通知地址不能为空");
}
System.out.println("wechatConfig().getAppid():"+wechatConfig().getAppid());
// 2. 构建参数
Map<String, String> params = new HashMap<>();
params.put("appid", wechatConfig().getAppid());

View File

@ -89,6 +89,8 @@ public class GoodsOrder extends BaseEntity
@Excel(name = "优惠券id")
private Long couponId;
/** 抵扣金额 */
@Excel(name = "抵扣金额")
private BigDecimal deduction;

View File

@ -21,6 +21,10 @@ public interface GoodsOrderMapper
public GoodsOrder selectGoodsOrderById(Long id);
public GoodsOrder selectGoodsOrderByorderId(String orderId);
public int selectCountGoodsOrderByUid(@Param("uid") Long uid, @Param("status") Long status);
public int selectAllCountGoodsOrderByUid(Long uid);

View File

@ -19,6 +19,9 @@ public interface UserMemberRechargeLogMapper
*/
public UserMemberRechargeLog selectUserMemberRechargeLogById(Integer id);
public UserMemberRechargeLog selectUserMemberRechargeLogByorderid(String orderid);
/**
* 查询用户充值记录列表
*

View File

@ -27,7 +27,7 @@ public interface IGoodsOrderService
*/
public List<GoodsOrder> selectGoodsOrderList(GoodsOrder goodsOrder);
public GoodsOrder selectGoodsOrderByorderId(String orderId);
public int selectCountGoodsOrderByUid(Long uid,Long status);

View File

@ -19,6 +19,9 @@ public interface IUserMemberRechargeLogService
*/
public UserMemberRechargeLog selectUserMemberRechargeLogById(Integer id);
public UserMemberRechargeLog selectUserMemberRechargeLogByorderid(String orderid);
/**
* 查询用户充值记录列表
*

View File

@ -32,6 +32,11 @@ public class GoodsOrderServiceImpl implements IGoodsOrderService
}
public GoodsOrder selectGoodsOrderByorderId(String orderId){
return goodsOrderMapper.selectGoodsOrderByorderId(orderId);
}
public int selectCountGoodsOrderByUid(Long uid,Long status) {
return goodsOrderMapper.selectCountGoodsOrderByUid(uid,status);

View File

@ -31,6 +31,12 @@ public class UserMemberRechargeLogServiceImpl implements IUserMemberRechargeLogS
return userMemberRechargeLogMapper.selectUserMemberRechargeLogById(id);
}
public UserMemberRechargeLog selectUserMemberRechargeLogByorderid(String orderid){
return userMemberRechargeLogMapper.selectUserMemberRechargeLogByorderid(orderid);
}
/**
* 查询用户充值记录列表
*

View File

@ -62,6 +62,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
order by id desc
</select>
<select id="selectGoodsOrderByorderId" parameterType="String" resultMap="GoodsOrderResult">
<include refid="selectGoodsOrderVo"/>
where order_id = #{orderId} limit 1
</select>
<select id="selectGoodsOrderById" parameterType="Long" resultMap="GoodsOrderResult">
<include refid="selectGoodsOrderVo"/>
where id = #{id}

View File

@ -33,7 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<where>
<if test="uid != null "> and uid = #{uid}</if>
</where>
order by updated_at desc
order by id desc
</select>
<select id="selectUserMemberRechargeLogById" parameterType="Integer" resultMap="UserMemberRechargeLogResult">
@ -41,6 +41,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{id}
</select>
<select id="selectUserMemberRechargeLogByorderid" parameterType="String" resultMap="UserMemberRechargeLogResult">
<include refid="selectUserMemberRechargeLogVo"/>
where orderid = #{orderid} limit 1
</select>
<insert id="insertUserMemberRechargeLog" parameterType="UserMemberRechargeLog" useGeneratedKeys="true" keyProperty="id">
insert into user_member_recharge_log
<trim prefix="(" suffix=")" suffixOverrides=",">

View File

@ -32,7 +32,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="uid != null "> and uid = #{uid}</if>
<if test="del != null "> and del = #{del}</if>
</where>
order by updated_at desc
order by id desc
</select>
<select id="selectUserMemnerConsumptionLogById" parameterType="Integer" resultMap="UserMemnerConsumptionLogResult">

View File

@ -248,9 +248,9 @@
<!-- 日志记录弹窗 -->
<el-dialog :title="logTitle" :visible.sync="logVisible" width="1000px" append-to-body>
<!-- 统计信息卡片 -->
<el-row :gutter="16" class="mb8">
<el-row :gutter="12" class="mb8">
<el-col :span="8">
<el-card shadow="hover" class="summary-card income-card">
<div class="summary-card income-card">
<div class="summary-content">
<div class="summary-icon">
<i class="el-icon-plus"></i>
@ -260,10 +260,10 @@
<div class="summary-value">¥{{ formatMoney(totalIncome) }}</div>
</div>
</div>
</el-card>
</div>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="summary-card expense-card">
<div class="summary-card expense-card">
<div class="summary-content">
<div class="summary-icon">
<i class="el-icon-minus"></i>
@ -273,10 +273,10 @@
<div class="summary-value">¥{{ formatMoney(totalExpense) }}</div>
</div>
</div>
</el-card>
</div>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="summary-card net-card">
<div class="summary-card net-card">
<div class="summary-content">
<div class="summary-icon">
<i class="el-icon-d-arrow-right"></i>
@ -288,7 +288,7 @@
</div>
</div>
</div>
</el-card>
</div>
</el-col>
</el-row>
@ -301,14 +301,7 @@
<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"
@ -337,7 +330,7 @@
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="变动类型" align="center" width="80">
<el-table-column label="变动类型" align="center" width="130">
<template slot-scope="scope">
<el-tag
:type="getChangeTypeTag(scope.row)"
@ -395,16 +388,7 @@
</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">
@ -415,7 +399,7 @@
</template>
</el-table-column>
<el-table-column label="变动时间" align="center" width="160">
<el-table-column label="变动时间" align="center" width="180">
<template slot-scope="scope">
<div class="time-content">
<i class="el-icon-time"></i>
@ -486,11 +470,7 @@
<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>
@ -590,7 +570,6 @@ export default {
//
logFilter: {
type: '',
consumptionType: '',
dateRange: []
},
//
@ -825,12 +804,7 @@ export default {
});
}
//
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) {
@ -873,7 +847,8 @@ export default {
//
getChangeTypeTag(row) {
if (this.currentType === null) {
return 'info'; //
// type=1(绿)type=2()
return row.type === 1 ? 'success' : 'danger';
}
if (row.type === 1) { //
return 'success';
@ -884,6 +859,12 @@ export default {
//
getChangeTypeText(row) {
if (this.currentType === null) {
// type
if (row.type === 1) {
return '余额变动--增加';
} else if (row.type === 2) {
return '余额变动--减少';
}
return '余额变动';
}
if (row.type === 1) {
@ -895,9 +876,9 @@ export default {
//
getAmountValue(row) {
if (this.currentType === null) {
//
// type=1()type=2()
if (row.consumptionmoney) {
return row.type === 1 ? row.consumptionmoney : -row.consumptionmoney;
return row.type === 1 ? parseFloat(row.consumptionmoney) : -parseFloat(row.consumptionmoney);
}
return 0;
}
@ -925,30 +906,31 @@ export default {
// /
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}');
if (!dateTime) {
return '-';
}
//
const date = new Date(dateTime);
if (isNaN(date.getTime())) {
return '-';
}
//
const timeStr = dateTime.toString();
const hasTimeInfo = timeStr.includes(':') || timeStr.includes('T') || timeStr.length > 10;
if (hasTimeInfo) {
//
return this.parseTime(dateTime, '{y}-{m}-{d} {h}:{i}:{s}');
} else {
//
return this.parseTime(dateTime, '{y}-{m}-{d}');
}
},
/** 显示余额变动记录 */
showBalanceLogs(userId) {
@ -963,7 +945,6 @@ export default {
resetLogFilters() {
this.logFilter = {
type: '',
consumptionType: '',
dateRange: []
};
this.logQuery.pageNum = 1;
@ -1048,10 +1029,18 @@ export default {
.summary-card {
display: flex;
align-items: center;
padding: 8px 12px;
padding: 6px 10px;
border-radius: 4px;
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.06);
min-height: 60px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.08);
min-height: 45px;
background: #fff;
border: 1px solid #f0f0f0;
transition: all 0.3s;
}
.summary-card:hover {
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.12);
border-color: #e6f7ff;
}
.summary-content {
@ -1060,8 +1049,8 @@ export default {
}
.summary-icon {
font-size: 18px;
margin-right: 8px;
font-size: 16px;
margin-right: 6px;
color: #409eff;
}
@ -1071,13 +1060,13 @@ export default {
}
.summary-title {
font-size: 12px;
font-size: 11px;
color: #606266;
margin-bottom: 2px;
margin-bottom: 1px;
}
.summary-value {
font-size: 16px;
font-size: 14px;
font-weight: bold;
color: #303133;
}

View File

@ -53,8 +53,8 @@
<el-col :span="6">
<el-form-item label="发票类别" prop="category">
<el-select v-model="queryParams.category" placeholder="请选择发票类别" clearable>
<el-option label="个人" :value="1" />
<el-option label="企业" :value="2" />
<el-option label="个人" :value="0" />
<el-option label="企业" :value="1" />
</el-select>
</el-form-item>
</el-col>
@ -162,8 +162,8 @@
</el-table-column>
<el-table-column label="发票类别" align="center" width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.category === 1 ? 'info' : 'warning'" size="small">
{{ scope.row.category === 1 ? '个人' : '企业' }}
<el-tag :type="scope.row.category === 0 ? 'info' : 'warning'" size="small">
{{ scope.row.category === 0 ? '个人' : '企业' }}
</el-tag>
</template>
</el-table-column>
@ -294,8 +294,8 @@
<el-col :span="12">
<el-form-item label="发票类别" prop="category">
<el-select v-model="form.category" placeholder="请选择发票类别">
<el-option label="个人" :value="1" />
<el-option label="企业" :value="2" />
<el-option label="个人" :value="0" />
<el-option label="企业" :value="1" />
</el-select>
</el-form-item>
</el-col>
@ -317,7 +317,7 @@
</el-form-item>
<!-- 企业信息 -->
<div v-if="form.category === 2">
<div v-if="form.category === 1">
<el-divider content-position="left">企业税务信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
@ -439,8 +439,8 @@
</div>
<div class="detail-item">
<label>发票类别</label>
<el-tag :type="detailForm.category === 1 ? 'info' : 'warning'" size="small">
{{ detailForm.category === 1 ? '个人' : '企业' }}
<el-tag :type="detailForm.category === 0 ? 'info' : 'warning'" size="small">
{{ detailForm.category === 0 ? '个人' : '企业' }}
</el-tag>
</div>
<div class="detail-item">
@ -475,7 +475,7 @@
</el-col>
</el-row>
<div v-if="detailForm.category === 2" class="detail-section">
<div v-if="detailForm.category === 1" class="detail-section">
<h4>企业税务信息</h4>
<el-row :gutter="20">
<el-col :span="12">