Compare commits

...

17 Commits

Author SHA1 Message Date
dzq 3ee313b1a3 fix(DeadlineOrderJob): 优化逾期商品处理逻辑并添加用户姓名显示
使用 UpdateWrapper 替代直接更新实体来清除商品关联,确保字段正确设置为 null
在逾期通知和日志中添加用户姓名信息,提高可追溯性
2025-07-21 15:02:30 +08:00
dzq 9e9b3a5cb5 feat(DeadlineOrderJob): 添加逾期订单处理通知功能
- 在QywxScheduleJob中添加日志记录微信用户信息
- 重构DeadlineOrderJob,添加企业微信通知功能
- 将创建操作日志和发送通知逻辑提取为独立方法
- 修复订单创建时间引用错误问题
2025-07-21 11:11:18 +08:00
dzq 9f04fe0ce3 feat(智能柜): 添加归还期限功能并实现逾期订单处理
- 在smart_cabinet表添加return_deadline字段记录归还期限
- 在SmartCabinetEntity和SmartCabinetDTO中添加对应字段
- 新增DeadlineOrderJob定时任务处理逾期订单
- 修改订单商品状态枚举和SQL查询逻辑
- 调整订单商品状态查询条件为不等于2
2025-07-21 08:29:07 +08:00
dzq 4b055dac24 feat(订单): 添加订单退还状态字段
在OrderWithGoodsDTO中添加returnStatus字段用于标识订单退还状态
修改ShopOrderMapper查询语句,增加退还状态逻辑判断
2025-07-16 09:34:51 +08:00
dzq 91e61308a1 feat(用户余额): 为用户余额记录添加剩余余额字段
在用户余额记录表中添加 remain_amount 字段,用于记录本次充值后的剩余余额。同时在 DTO 和 Entity 中添加对应字段,保持数据模型一致性。
2025-07-08 10:16:46 +08:00
dzq 234441ff43 feat(订单查询): 重构订单查询逻辑并添加余额信息
- 使用 and/or 包装器重构订单查询条件,提高查询逻辑清晰度
- 在支付控制器中添加 ab98 用户余额信息返回
- 移除 hasReturn 条件,统一查询已支付订单
2025-07-07 17:48:25 +08:00
dzq 9b95bc75e7 feat(用户模块): 添加用户余额字段
在用户DTO、实体类和详情DTO中添加ab98Balance字段,并在数据库中添加对应列
2025-07-07 09:59:43 +08:00
dzq 1a6d627482 feat(用户余额): 新增用户余额交易记录功能
添加用户余额交易记录相关功能,包括实体类、Mapper、Service、Controller等组件
实现交易记录的增删改查及状态管理功能
移除数据库表中transaction_time字段及相关索引
2025-07-07 09:19:15 +08:00
dzq 27489e68eb fix(订单服务): 修复空订单列表导致的NPE问题
feat(数据库): 新增用户余额交易记录表

refactor(权限检查): 使用权限字符串替代路由树检查
2025-07-04 15:06:04 +08:00
dzq c33f8951d7 perf(退货审批): 添加性能监控日志记录各阶段执行时间
添加各关键步骤的执行时间记录,用于监控和优化退货审批流程的性能。记录包括模型加载、商品列表查询、库存操作等各阶段耗时,并写入操作日志以便后续分析。
2025-07-03 10:54:10 +08:00
dzq 11743ff77d feat(智能柜): 添加订单关联字段以支持订单信息展示
在CabinetCellEntity、CabinetDetailDTO和SmartCabinetApplicationService中添加orderId和orderGoodsId字段
修改CabinetCellMapper查询以获取关联订单信息
2025-07-03 09:35:38 +08:00
dzq ebe6ac21c6 feat(智能柜): 添加单元格价格字段并优化订单查询逻辑
- 在CabinetDetailDTO中添加cellPrice字段
- 优化订单查询逻辑,避免重复数据
- 在GetBalanceResponse中添加ab98User信息
- 调整PaymentController中用户信息处理顺序
2025-07-02 15:42:22 +08:00
dzq e48735a463 feat(订单和机柜): 添加用户租赁机柜查询和订单筛选功能
添加根据用户ID和企业ID查询租赁机柜的功能
在订单查询接口中增加hasReturn参数用于筛选已归还/未归还订单
在QywxMessageJob中添加支付状态检查逻辑
2025-07-02 11:28:40 +08:00
dzq a068d54493 fix(租柜单元格): 修复查询租用单元格最新订单时的空列表问题
当租用单元格列表为空时,避免执行不必要的查询操作
2025-07-01 15:28:09 +08:00
dzq 2bf2494383 feat(柜机格口): 添加获取格口最新订单信息功能
新增CabinetCellLatestOrderDTO类用于存储格口最新订单信息
在CabinetCellService中新增selectLatestOrderInfoByCell方法查询格口最新订单
修改CabinetCellWithOrderCountDTO添加订单相关字段
优化CabinetCellApplicationService.getCabinetCellList方法合并订单信息
2025-07-01 11:38:20 +08:00
dzq 2c7a336ae9 feat(企业微信): 添加逾期商品提醒功能
新增企业微信定时任务,每天检查逾期未归还商品并发送提醒
在用户服务中添加通过手机号查询用户的方法
修改未归还商品查询接口,支持按企业ID过滤
2025-07-01 08:27:16 +08:00
dzq 929b70897f fix: 修复企业微信用户查询并添加模拟登录接口
修复企业微信用户查询SQL未过滤已删除和禁用用户的问题
添加模拟企业微信登录接口用于测试环境
2025-06-30 15:40:06 +08:00
50 changed files with 1488 additions and 81 deletions

View File

@ -0,0 +1,67 @@
package com.agileboot.admin.controller.ab98.balance;
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.enums.common.BusinessTypeEnum;
import com.agileboot.domain.ab98.balance.UserBalanceRecordApplicationService;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.query.SearchUserBalanceRecordQuery;
import com.agileboot.domain.common.command.BulkOperationCommand;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ab98/balance-records")
@RequiredArgsConstructor
@Validated
public class UserBalanceRecordController extends BaseController {
private final UserBalanceRecordApplicationService userBalanceRecordApplicationService;
@Operation(summary = "余额记录列表")
@GetMapping
public ResponseDTO<PageDTO<UserBalanceRecordEntity>> list(SearchUserBalanceRecordQuery<UserBalanceRecordEntity> query) {
PageDTO<UserBalanceRecordEntity> page = userBalanceRecordApplicationService.getUserBalanceRecordList(query);
return ResponseDTO.ok(page);
}
@Operation(summary = "新增余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.ADD)
@PostMapping
public ResponseDTO<Void> add(@Validated @RequestBody AddUserBalanceRecordCommand command) {
userBalanceRecordApplicationService.addUserBalanceRecord(command);
return ResponseDTO.ok();
}
@Operation(summary = "修改余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.MODIFY)
@PutMapping("/{id}")
public ResponseDTO<Void> edit(@PathVariable Long id, @Validated @RequestBody UpdateUserBalanceRecordCommand command) {
command.setBalanceRecordId(id);
userBalanceRecordApplicationService.updateUserBalanceRecord(command);
return ResponseDTO.ok();
}
@Operation(summary = "删除余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.DELETE)
@DeleteMapping("/{ids}")
public ResponseDTO<Void> remove(@PathVariable @NotNull List<Long> ids) {
userBalanceRecordApplicationService.deleteUserBalanceRecord(new BulkOperationCommand<>(ids));
return ResponseDTO.ok();
}
}

View File

@ -1,6 +1,8 @@
package com.agileboot.admin.controller.shop;
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
import com.agileboot.common.constant.Constants;
import com.agileboot.common.constant.WeixinConstants;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
@ -26,16 +28,10 @@ import java.math.BigDecimal;
import java.util.List;
import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/shop/shops")
@ -53,7 +49,10 @@ public class ShopController extends BaseController {
@Operation(summary = "首页数据")
@PreAuthorize("@permission.has('welcome:info')")
@GetMapping("/Stats")
public ResponseDTO<StatsDTO> stats() {
public ResponseDTO<StatsDTO> stats(@RequestParam String corpid) {
if (StringUtils.isBlank(corpid)) {
corpid = WeixinConstants.corpid;
}
// 创建统计数据DTO对象
StatsDTO statsDTO = new StatsDTO();
// 设置商店总数调用商店服务统计
@ -67,7 +66,7 @@ public class ShopController extends BaseController {
// 设置订单总金额调用订单服务计算
statsDTO.setOrderAmountSum(orderApplicationService.sumTotalAmount());
List<ShopOrderGoodsEntity> unReturnOrderGoods = orderApplicationService.selectUnReturnOrderGoods();
List<ShopOrderGoodsEntity> unReturnOrderGoods = orderApplicationService.selectUnReturnOrderGoods(corpid);
// 设置未还商品数量调用订单服务统计
statsDTO.setUnReturnedGoodsCount((long) unReturnOrderGoods.size());
// 设置未还订单数量调用订单服务统计

View File

@ -459,6 +459,8 @@ public class QywxScheduleJob {
List<UserListResponse.UserInfo> toAdd = wxUsers.stream()
.filter(wxUser -> !existingUserIds.contains(wxUser.getUserid()))
.collect(Collectors.toList());
log.info("wxUsers: {}", JSONUtil.toJsonStr(wxUsers));
log.info("existingUserIds: {}", JSONUtil.toJsonStr(existingUserIds));
log.info("syncUserInfo toAdd: {}", JSONUtil.toJsonStr(toAdd));
// 识别需要删除的用户

View File

@ -0,0 +1,215 @@
package com.agileboot.admin.customize.service.job;
import cn.hutool.core.date.DateUtil;
import com.agileboot.domain.cabinet.cell.db.CabinetCellEntity;
import com.agileboot.domain.cabinet.cell.db.CabinetCellService;
import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetEntity;
import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetService;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
import com.agileboot.domain.qywx.api.QywxApiUtil;
import com.agileboot.domain.qywx.api.response.NewsArticle;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import com.agileboot.domain.shop.order.db.ShopOrderEntity;
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
import com.agileboot.domain.shop.order.db.ShopOrderGoodsService;
import com.agileboot.domain.shop.order.db.ShopOrderService;
import com.agileboot.domain.shop.paymentOperationLog.PaymentOperationLogApplicationService;
import com.agileboot.domain.shop.paymentOperationLog.command.AddPaymentOperationLogCommand;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RequiredArgsConstructor
@Component
@Slf4j
public class DeadlineOrderJob {
private final ShopOrderService shopOrderService;
private final ShopOrderGoodsService shopOrderGoodsService;
private final CabinetCellService cabinetCellService;
private final SmartCabinetService smartCabinetService;
private final PaymentOperationLogApplicationService paymentOperationLogApplicationService;
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
private final AccessTokenApplicationService accessTokenApplicationService;
private final QyUserService qyUserService;
/**
* 定时任务处理超过归还期限的订单商品
* 每天凌晨3点执行检查所有处于借出中状态的订单商品是否逾期并进行处理
*/
@Scheduled(cron = "0 50 * * * *") // 每小时第50分钟执行
@Transactional // 确保任务的原子性如果处理过程中出现异常数据不会被部分更新
public void markOverdueOrdersAndRemoveFromCabinet() {
log.info("开始执行逾期订单处理及商品下架任务...");
try {
// 1. 查询所有处于正常/借出中状态且关联了格口ID的订单商品
// 假设 ShopOrderGoodsEntity.STATUS_NORMAL_BORROWED = 1
// 并且只处理借还模式ShopOrderEntity.mode = 2的订单商品
// 仅选择 payStatus 2 (已支付) 的订单商品进行处理因为未支付的订单不应该算作借出
LambdaQueryWrapper<ShopOrderGoodsEntity> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShopOrderGoodsEntity::getStatus, ShopOrderGoodsEntity.STATUS_NORMAL_BORROWED); // Assuming 1 means "borrowed"
queryWrapper.isNotNull(ShopOrderGoodsEntity::getCellId); // 必须在格口中
List<ShopOrderGoodsEntity> borrowedGoodsList = shopOrderGoodsService.list(queryWrapper);
if (CollectionUtils.isEmpty(borrowedGoodsList)) {
log.info("未找到需要检查的借出中商品记录.");
return;
}
log.info("查找到 {} 条需检查的借出中商品记录.", borrowedGoodsList.size());
for (ShopOrderGoodsEntity orderGoods : borrowedGoodsList) {
// 获取关联的订单信息
ShopOrderEntity order = shopOrderService.getById(orderGoods.getOrderId());
if (order == null) {
log.warn("订单商品[{}]关联的订单[{}]不存在,跳过处理.", orderGoods.getOrderGoodsId(), orderGoods.getOrderId());
continue;
}
// 确保是借还模式
if (order.getMode() != 0) { // 0-支付模式
continue;
}
// 确保订单已支付
if (order.getPayStatus() != 2) { // 2-已支付
log.debug("订单[{}]未支付,订单商品[{}]跳过处理.", order.getOrderId(), orderGoods.getOrderGoodsId());
continue;
}
// 获取关联的格口信息
CabinetCellEntity cabinetCell = cabinetCellService.getById(orderGoods.getCellId());
if (cabinetCell == null) {
log.warn("订单商品[{}]关联的格口[{}]不存在,跳过处理.", orderGoods.getOrderGoodsId(), orderGoods.getCellId());
continue;
}
if (!cabinetCell.getStock().equals(0)) {
log.warn("订单商品[{}]关联的格口[{}]库存不为0跳过处理.", orderGoods.getOrderGoodsId(), cabinetCell.getCellId());
continue;
}
// 获取关联的智能柜信息以获取归还期限
SmartCabinetEntity smartCabinet = smartCabinetService.getById(cabinetCell.getCabinetId());
if (smartCabinet == null) {
log.warn("格口[{}]关联的智能柜[{}]不存在,跳过处理.", cabinetCell.getCellId(), cabinetCell.getCabinetId());
continue;
}
Integer returnDeadlineDays = smartCabinet.getReturnDeadline();
// 检查归还期限是否有效大于0天
if (returnDeadlineDays == null || returnDeadlineDays <= 0) {
continue;
}
Date orderCreateTime = order.getCreateTime();
if (orderCreateTime == null) {
log.warn("订单[{}]的创建时间为空,无法计算归还期限,订单商品[{}]跳过处理.", order.getOrderId(), orderGoods.getOrderGoodsId());
continue;
}
// 计算归还截止日期
Date deadlineDate = DateUtil.offsetDay(orderCreateTime, returnDeadlineDays);
Date now = new Date(); // 当前时间
// 如果当前时间晚于归还截止日期则商品已逾期
if (now.after(deadlineDate)) {
log.info("发现逾期订单商品:订单号[{}], 商品ID[{}], 名称[{}]。创建时间[{}], 归还期限[{}天], 截止日期[{}].",
order.getOrderId(), orderGoods.getOrderGoodsId(), orderGoods.getGoodsName(),
DateUtil.formatDateTime(orderCreateTime), returnDeadlineDays, DateUtil.formatDateTime(deadlineDate));
// 1. 更新订单商品状态为已逾期
orderGoods.setStatus(ShopOrderGoodsEntity.STATUS_OVERDUE); // STATUS_OVERDUE = 7
shopOrderGoodsService.updateById(orderGoods); // 更新数据库
// 2. 将商品从智能柜格口下架
// 使用 UpdateWrapper 更新字段为 null
UpdateWrapper<CabinetCellEntity> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("cell_id", cabinetCell.getCellId()); // 根据格口 ID 确定更新记录
updateWrapper.set("goods_id", null); // 设置商品 ID null
updateWrapper.set("usage_status", 1); // 设置使用状态为 1-空闲
cabinetCellService.update(updateWrapper); // 执行更新操作
log.info("成功处理逾期商品订单商品ID[{}]状态更新为已逾期并从格口ID[{}]下架。", orderGoods.getOrderGoodsId(), cabinetCell.getCellId());
// 创建操作日志
createPaymentOperationLog(order, orderGoods, returnDeadlineDays, deadlineDate);
// 发送审核消息
try {
String appid = "QWTONG_YS_WXSHOP";
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid);
QyAuthCorpInfoEntity authCorpInfo = authCorpInfoList.stream()
.filter(a -> order.getCorpid().equals(a.getCorpid()))
.findFirst().orElse(null);
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
sendReviewNotification(order, orderGoods, authCorpInfo, accessToken, returnDeadlineDays, deadlineDate);
} catch (Exception e) {
log.error("发送逾期未归还通知失败", e);
}
}
}
} catch (Exception globalException) {
// 记录整个定时任务的全局错误
log.error("执行逾期订单处理及商品下架任务时发生全局错误:", globalException);
}
log.info("逾期订单处理及商品下架任务执行完毕.");
}
/**
* 创建逾期订单操作日志
*/
private void createPaymentOperationLog(ShopOrderEntity order, ShopOrderGoodsEntity orderGoods, int returnDeadlineDays, Date deadlineDate) {
try {
AddPaymentOperationLogCommand paymentOperationLogCommand = new AddPaymentOperationLogCommand();
paymentOperationLogCommand.setOperationType("DeadlineOrder");
paymentOperationLogCommand.setStatus(1);
paymentOperationLogCommand.setRemark(
String.format("逾期订单商品:用户姓名[%s], 订单号[%s], 商品ID[%s], 名称[%s]。创建时间[%s], 归还期限[%s天], 截止日期[%s].", order.getName(),
order.getOrderId(), orderGoods.getOrderGoodsId(), orderGoods.getGoodsName(), DateUtil.formatDateTime(order.getCreateTime()), returnDeadlineDays, DateUtil.formatDateTime(deadlineDate)));
paymentOperationLogCommand.initBaseEntity();
paymentOperationLogApplicationService.addPaymentOperationLog(paymentOperationLogCommand);
} catch (Exception e) {
log.error("处理逾期订单商品[{}]时创建退款操作日志失败.", orderGoods.getOrderGoodsId(), e);
}
}
/**
* 发送逾期商品审核通知
*/
private void sendReviewNotification(ShopOrderEntity order, ShopOrderGoodsEntity orderGoods, QyAuthCorpInfoEntity authCorpInfo, QyAccessTokenEntity accessToken, int returnDeadlineDays, Date deadlineDate) {
try {
List<String> adminUserIds = qyUserService.selectAdminUserIds(order.getCorpid());
String toUser = String.join("|", adminUserIds);
String toparty = "";
String totag = "";
List<NewsArticle> articles = new ArrayList<>();
NewsArticle article = new NewsArticle();
article.setTitle("逾期商品自动下架通知");
article.setDescription(String.format("用户姓名: %s, 订单号: %s, 商品名称: %s, 创建时间: %s, 归还期限: %s天, 截止日期: %s", order.getName(),
order.getOrderId(), orderGoods.getGoodsName(), DateUtil.formatDateTime(order.getCreateTime()), returnDeadlineDays, DateUtil.formatDateTime(deadlineDate)));
article.setPicurl(orderGoods.getCoverImg());
article.setUrl("http://wxshop.ab98.cn/shop-api/api/shop/qy/wechatAuth");
articles.add(article);
QywxApiUtil.sendNewsMessage(accessToken.getAccessToken(), Integer.valueOf(authCorpInfo.getAgentid()),
toUser, toparty, totag, articles);
} catch (Exception e) {
log.error("发送逾期未归还通知失败", e);
}
}
}

View File

@ -0,0 +1,179 @@
package com.agileboot.admin.customize.service.job;
import cn.hutool.core.date.DateUtil;
import com.agileboot.domain.ab98.user.db.Ab98UserService;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
import com.agileboot.domain.qywx.api.QywxApiUtil;
import com.agileboot.domain.qywx.api.response.NewsArticle;
import com.agileboot.domain.qywx.authCorpInfo.AuthCorpInfoApplicationService;
import com.agileboot.domain.qywx.authCorpInfo.db.QyAuthCorpInfoEntity;
import com.agileboot.domain.qywx.department.DepartmentApplicationService;
import com.agileboot.domain.qywx.template.TemplateApplicationService;
import com.agileboot.domain.qywx.template.command.UpdateTemplateCommand;
import com.agileboot.domain.qywx.template.db.QyTemplateEntity;
import com.agileboot.domain.qywx.user.QyUserApplicationService;
import com.agileboot.domain.qywx.user.db.QyUserEntity;
import com.agileboot.domain.qywx.user.db.QyUserService;
import com.agileboot.domain.qywx.userQySys.SysUserQyUserApplicationService;
import com.agileboot.domain.ab98.user.db.Ab98UserEntity;
import com.agileboot.domain.shop.order.OrderApplicationService;
import com.agileboot.domain.shop.order.db.ShopOrderEntity;
import com.agileboot.domain.shop.order.db.ShopOrderGoodsEntity;
import com.agileboot.domain.shop.order.db.ShopOrderService;
import com.agileboot.domain.system.role.db.SysRoleService;
import com.agileboot.domain.system.user.model.UserModelFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Component
@Slf4j
public class QywxMessageJob {
private final TemplateApplicationService templateApplicationService;
private final AuthCorpInfoApplicationService authCorpInfoApplicationService;
private final AccessTokenApplicationService accessTokenApplicationService;
private final DepartmentApplicationService departmentApplicationService;
private final QyUserApplicationService qyUserApplicationService;
private final SysUserQyUserApplicationService sysUserQyUserApplicationService;
private final UserModelFactory userModelFactory;
private final SysRoleService sysRoleService;
private final OrderApplicationService orderApplicationService;
private final ShopOrderService orderService;
private final Ab98UserService ab98UserService;
private final QyUserService qyUserService;
// private static final String appid = "QYTONG_YS_WXSHOP";
/**
* 企业微信应用ID常量
* 用于标识当前集成的第三方应用对应企业微信服务商后台配置的应用凭证
*/
private static final String appid2 = "QWTONG_YS_WXSHOP";
/**
* 每天向逾期未归还商品的用户发送提醒消息
* 执行时间每天凌晨2点
*/
@Scheduled(cron = "0 0 10 * * *")
public void sendOverdueGoodsReminderTask() {
try {
// 1. 获取所有已授权企业信息
List<QyAuthCorpInfoEntity> authCorpInfoList = authCorpInfoApplicationService.getByAppid(appid2);
if (CollectionUtils.isEmpty(authCorpInfoList)) {
log.info("没有找到已授权的企业信息");
return;
}
// 2. 遍历企业处理
for (QyAuthCorpInfoEntity authCorpInfo : authCorpInfoList) {
sendOverdueReminderForCorp(appid2, authCorpInfo);
}
} catch (Exception e) {
log.error("发送逾期商品提醒任务失败", e);
}
}
/**
* 为单个企业发送逾期提醒
*/
private void sendOverdueReminderForCorp(String appid, QyAuthCorpInfoEntity authCorpInfo) {
try {
// 3. 获取企业访问令牌
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(appid, authCorpInfo.getCorpid());
if (accessToken == null || StringUtils.isBlank(accessToken.getAccessToken())) {
log.error("企业[{}]获取accessToken失败", authCorpInfo.getCorpid());
return;
}
// 4. 查询逾期未归还商品记录需实现商品借阅相关Service
List<ShopOrderGoodsEntity> unReturnOrderGoods = orderApplicationService.selectUnReturnOrderGoods(authCorpInfo.getCorpid());
if (CollectionUtils.isEmpty(unReturnOrderGoods)) {
log.info("企业[{}]没有未归还商品记录", authCorpInfo.getCorpid());
return;
}
// 5. 筛选createTime超过一周的记录
Date oneWeekAgo = DateUtil.offsetDay(new Date(), -7);
List<ShopOrderGoodsEntity> overdueRecords = unReturnOrderGoods.stream()
.filter(record -> record.getCreateTime().before(oneWeekAgo))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(overdueRecords)) {
log.info("企业[{}]没有超过一周未归还的商品记录", authCorpInfo.getCorpid());
return;
}
// 6. 遍历逾期记录发送消息
for (ShopOrderGoodsEntity record : overdueRecords) {
sendSingleReminder(accessToken.getAccessToken(), authCorpInfo, record);
}
} catch (Exception e) {
log.error("企业[{}]发送逾期提醒失败", authCorpInfo.getCorpid(), e);
}
}
/**
* 发送单个逾期提醒消息
*/
private void sendSingleReminder(String accessToken, QyAuthCorpInfoEntity authCorpInfo, ShopOrderGoodsEntity record) {
try {
ShopOrderEntity order = orderService.getById(record.getOrderId());
if (order.getMode().equals(3) || order.getMode().equals(4)) {
log.info("订单[{}]为租赁订单,无需发送逾期提醒", order.getOrderId());
return;
}
if (!order.getPayStatus().equals(2)) {
log.info("订单[{}]未支付,无需发送逾期提醒", order.getOrderId());
return;
}
// 6. 获取用户信息
List<QyUserEntity> userList = qyUserApplicationService.getUserByUserId(order.getUserid());
QyUserEntity user = null;
if (CollectionUtils.isEmpty(userList)) {
if (StringUtils.isBlank(order.getMobile())) {
log.error("用户[{}]不存在企业微信信息", order.getName());
return;
}
Ab98UserEntity ab98User = ab98UserService.selectByTel(order.getMobile());
user = qyUserService.getUserByAb98UserId(ab98User.getAb98UserId(), authCorpInfo.getCorpid());
} else {
user = userList.get(0);
}
if (user == null || StringUtils.isBlank(user.getUserid())) {
log.error("用户[{}]不存在企业微信信息", order.getName());
return;
}
// 发送审核消息
try {
// 获取用户ID
String toUser = user.getUserid();
String toparty = "";
String totag = "";
List<NewsArticle> articles = new ArrayList<>();
NewsArticle article = new NewsArticle();
article.setTitle("逾期未归还通知");
article.setDescription("你借用的 【" + record.getGoodsName() + "】 已逾期未归还,请及时归还");
article.setPicurl(record.getCoverImg());
article.setUrl("http://wxshop.ab98.cn/shop-api/api/shop/qy/wechatAuth/home");
articles.add(article);
QywxApiUtil.sendNewsMessage(accessToken, Integer.valueOf(authCorpInfo.getAgentid()),
toUser, toparty, totag, articles);
} catch (Exception e) {
log.error("发送退货审核通知失败", e);
}
} catch (Exception e) {
log.error("发送单个逾期提醒失败", e);
}
}
}

View File

@ -45,16 +45,36 @@ public class CabinetCellController {
private final CabinetMainboardModelFactory cabinetMainboardModelFactory;
private final CabinetCellApplicationService cabinetCellApplicationService;
/**
* 管理员获取机柜格口列表
* @param shopId
* @return
*/
@GetMapping("/detail")
public ResponseDTO<List<CabinetDetailDTO>> getCabinetDetail(@RequestParam Long shopId) {
return ResponseDTO.ok(smartCabinetApplicationService.getCabinetDetail(shopId));
}
/**
* 获取正在出租的机柜格口列表
* @param shopId
* @return
*/
@GetMapping("/detail/renting")
public ResponseDTO<List<RentingCabinetDetailDTO>> getRentingCabinetDetail(@RequestParam Long shopId) {
return ResponseDTO.ok(smartCabinetApplicationService.getRentingCabinetDetail(shopId));
}
/**
* 用户获取自己租用的机柜格口列表
* @param corpid
* @param ab98UserId
* @return
*/
@GetMapping("/detail/user")
public ResponseDTO<List<CabinetDetailDTO>> getCabinetDetail(@RequestParam String corpid, @RequestParam Long ab98UserId) {
return ResponseDTO.ok(smartCabinetApplicationService.getUserRentedCabinetList(corpid, ab98UserId));
}
@Operation(summary = "配置格口商品库存")
@PutMapping("/configureGoodsCellsStock/{cellId}/{goodsId}/{stock}")

View File

@ -84,8 +84,10 @@ public class OrderController extends BaseController {
* @return 包含订单列表的响应
*/
@GetMapping("/user/{openid}")
public ResponseDTO<GetOrdersByOpenIdDTO> getOrdersByOpenId(@PathVariable String openid, @RequestParam(required = false) String corpid) {
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByOpenId(openid, corpid);
public ResponseDTO<GetOrdersByOpenIdDTO> getOrdersByOpenId(@PathVariable String openid,
@RequestParam(required = false) String corpid,
@RequestParam(required = false) Integer hasReturn) {
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByOpenId(openid, corpid, hasReturn);
return ResponseDTO.ok(result);
}
@ -94,8 +96,9 @@ public class OrderController extends BaseController {
* @return 包含订单列表的响应
*/
@GetMapping("/user/qy/{qyUserId}")
public ResponseDTO<GetOrdersByOpenIdDTO> getOrdersByOpenId(@PathVariable Long qyUserId) {
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByQyUserId(qyUserId);
public ResponseDTO<GetOrdersByOpenIdDTO> getOrdersByOpenId(@PathVariable Long qyUserId,
@RequestParam(required = false) Integer hasReturn) {
GetOrdersByOpenIdDTO result = orderApplicationService.getOrdersByQyUserId(qyUserId, hasReturn);
return ResponseDTO.ok(result);
}

View File

@ -190,12 +190,60 @@ public class PaymentController {
SystemLoginUser loginUser = new SystemLoginUser();
loginUser.setAdmin(false);
loginUser.setUserId(sysUserEntity.getUserId());
List<RouterDTO> routerTree = menuApplicationService.getRouterTree(loginUser);
log.info("getRouterTreeuserid: {}, routerTree: {}", userid, JSONUtil.toJsonStr(routerTree));
List<String> permissions = menuApplicationService.getPermissions(loginUser);
/* List<RouterDTO> routerTree = menuApplicationService.getRouterTree(loginUser);
log.info("getRouterTreeuserid: {}, routerTree: {}", userid, JSONUtil.toJsonStr(routerTree));*/
int isCabinetAdmin = 0;
if (routerTree != null && !routerTree.isEmpty()) {
isCabinetAdmin = isCabinetAdmin(routerTree)
// if (routerTree != null && !routerTree.isEmpty()) {
// isCabinetAdmin = isCabinetAdmin(routerTree)
// ? 1 : 0;
// }
if (permissions != null && !permissions.isEmpty()) {
isCabinetAdmin = permissions.contains("shop:cabinet:write")
? 1 : 0;
}
OpenidResponse openidResponse = QywxApiUtil.convertToOpenid(qyAccessToken.getAccessToken(), userid);
QyUserEntity qyUserEntity = qyUserApplicationService.getUserByUserIdAndCorpid(userid, corpid);
Ab98UserEntity ab98User = null;
if (qyUserEntity != null && qyUserEntity.getAb98UserId() != null) {
ab98User = ab98UserApplicationService.getByAb98UserId(qyUserEntity.getAb98UserId());
}
return ResponseDTO.ok(new QyLoginDTO(userid, openidResponse.getOpenid(), isCabinetAdmin,
qyUserEntity == null ? 0 : qyUserEntity.getId(),
qyUserEntity == null ? "" : qyUserEntity.getName(),
ab98User));
} catch (RestClientException e) {
log.error("qyLogin失败", e);
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "微信服务调用失败"));
}
}
@GetMapping("/login/qy/fake")
public ResponseDTO<QyLoginDTO> fakeQyLogin(String corpid, String userid) {
try {
QyAccessTokenEntity qyAccessToken = accessTokenApplicationService.getByAppid("QWTONG_YS_WXSHOP", corpid);
// 通过企业微信code获取用户ID
// 根据企业微信用户ID查询系统用户名
SysUserEntity sysUserEntity = sysUserQyUserApplicationService.getSysUserByQyUserid(userid);
SystemLoginUser loginUser = new SystemLoginUser();
loginUser.setAdmin(false);
loginUser.setUserId(sysUserEntity.getUserId());
List<String> permissions = menuApplicationService.getPermissions(loginUser);
/* List<RouterDTO> routerTree = menuApplicationService.getRouterTree(loginUser);
log.info("getRouterTreeuserid: {}, routerTree: {}", userid, JSONUtil.toJsonStr(routerTree));*/
int isCabinetAdmin = 0;
// if (routerTree != null && !routerTree.isEmpty()) {
// isCabinetAdmin = isCabinetAdmin(routerTree)
// ? 1 : 0;
// }
if (permissions != null && !permissions.isEmpty()) {
isCabinetAdmin = permissions.contains("shop:cabinet:write")
? 1 : 0;
}
@ -255,9 +303,9 @@ public class PaymentController {
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "无效的openid参数"));
}
Ab98UserEntity ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId());
try {
if (qyUser.getAb98UserId()!= null) {
Ab98UserEntity ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId());
ab98User.setOpenid(openid);
ab98User.updateById();
}
@ -271,7 +319,8 @@ public class PaymentController {
qyUser.getCorpid(),
qyUser.getBalance(),
qyUser.getUseBalance(),
qyUser.getBalanceLimit());
qyUser.getBalanceLimit(),
ab98User);
return ResponseDTO.ok(response);
}
@ -285,13 +334,19 @@ public class PaymentController {
public ResponseDTO<GetBalanceResponse> getBalanceByQyUserid(@RequestParam String corpid, @RequestParam String userid) {
QyUserEntity qyUser = qyUserApplicationService.getUserByUserIdAndCorpid(userid, corpid);
Ab98UserEntity ab98User = null;
if (qyUser.getAb98UserId()!= null) {
ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId());
}
// 创建响应对象假设GetBalanceResponse包含balance字段
GetBalanceResponse response = new GetBalanceResponse(
qyUser.getUserid(),
qyUser.getCorpid(),
qyUser.getBalance(),
qyUser.getUseBalance(),
qyUser.getBalanceLimit());
qyUser.getBalanceLimit(),
ab98User);
return ResponseDTO.ok(response);
}

View File

@ -1,5 +1,6 @@
package com.agileboot.api.response;
import com.agileboot.domain.ab98.user.db.Ab98UserEntity;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -13,4 +14,5 @@ public class GetBalanceResponse {
private BigDecimal balance;
private BigDecimal useBalance;
private BigDecimal balanceLimit;
private Ab98UserEntity ab98User;
}

View File

@ -0,0 +1,68 @@
package com.agileboot.domain.ab98.balance;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import com.agileboot.domain.ab98.balance.model.UserBalanceRecordModel;
import com.agileboot.domain.ab98.balance.model.UserBalanceRecordModelFactory;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
@RequiredArgsConstructor
public class UserBalanceRecordApplicationService {
private final UserBalanceRecordService userBalanceRecordService;
private final UserBalanceRecordModelFactory userBalanceRecordModelFactory;
public PageDTO<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query) {
Page<UserBalanceRecordEntity> page = userBalanceRecordService.getUserBalanceRecordList(query);
List<UserBalanceRecordEntity> records = page.getRecords();
return new PageDTO<>(records, page.getTotal());
}
public void addUserBalanceRecord(AddUserBalanceRecordCommand command) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.create();
model.loadAddCommand(command);
model.insert();
}
public void updateUserBalanceRecord(UpdateUserBalanceRecordCommand command) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.loadById(command.getBalanceRecordId());
model.loadUpdateCommand(command);
model.updateById();
}
public void deleteUserBalanceRecord(BulkOperationCommand<Long> command) {
for (Long id : command.getIds()) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.loadById(id);
model.deleteById();
}
}
public List<UserBalanceRecordEntity> selectAllSuccessRecords() {
return userBalanceRecordService.selectAllSuccessRecords();
}
public UserBalanceRecordEntity getFirstSuccessRecord() {
return userBalanceRecordService.getFirstSuccessRecord();
}
public UserBalanceRecordEntity getByAb98UserId(Long ab98UserId) {
return userBalanceRecordService.getByAb98UserId(ab98UserId);
}
public UserBalanceRecordEntity getByTradeId(String tradeId) {
return userBalanceRecordService.getByTradeId(tradeId);
}
}

View File

@ -0,0 +1,11 @@
package com.agileboot.domain.ab98.balance.command;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AddUserBalanceRecordCommand extends UserBalanceRecordEntity {
}

View File

@ -0,0 +1,16 @@
package com.agileboot.domain.ab98.balance.command;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UpdateUserBalanceRecordCommand extends AddUserBalanceRecordCommand {
@NotNull
@PositiveOrZero
private Long balanceRecordId;
}

View File

@ -0,0 +1,76 @@
package com.agileboot.domain.ab98.balance.db;
import com.agileboot.common.core.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 用户余额交易记录表
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
@Getter
@Setter
@TableName("user_balance_record")
@ApiModel(value = "UserBalanceRecordEntity对象", description = "用户余额交易记录表")
public class UserBalanceRecordEntity extends BaseEntity<UserBalanceRecordEntity> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键ID")
@TableId(value = "balance_record_id", type = IdType.AUTO)
private Long balanceRecordId;
@ApiModelProperty("汇邦云用户唯一ID")
@TableField("ab98_user_id")
private Long ab98UserId;
@ApiModelProperty("openid")
@TableField("openid")
private String openid;
@ApiModelProperty("交易类型1充值 2消费 3退款")
@TableField("transaction_type")
private Integer transactionType;
@ApiModelProperty("消费或退款时使用的充值id")
@TableField("use_record_id")
private Long useRecordId;
@ApiModelProperty("交易金额,单位分")
@TableField("amount")
private Integer amount;
@ApiModelProperty("交易状态0未完成 1成功 2失败")
@TableField("`status`")
private Integer status;
@ApiModelProperty("支付网关交易id")
@TableField("trade_id")
private String tradeId;
@ApiModelProperty("业务系统订单ID对接外部系统")
@TableField("biz_order_id")
private String bizOrderId;
@ApiModelProperty("本次充值剩余余额(单位:分)")
@TableField("remain_amount")
private Integer remainAmount;
@Override
public Serializable pkVal() {
return this.balanceRecordId;
}
}

View File

@ -0,0 +1,48 @@
package com.agileboot.domain.ab98.balance.db;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
* 用户余额交易记录表 Mapper 接口
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
public interface UserBalanceRecordMapper extends BaseMapper<UserBalanceRecordEntity> {
@Select("SELECT balance_record_id, ab98_user_id, openid, transaction_type, use_record_id, amount, status, trade_id, biz_order_id, create_time, update_time " +
"FROM user_balance_record " +
"${ew.customSqlSegment}")
Page<UserBalanceRecordEntity> getUserBalanceRecordList(
Page<UserBalanceRecordEntity> page,
@Param(Constants.WRAPPER) Wrapper<UserBalanceRecordEntity> queryWrapper
);
@Select("SELECT * " +
"FROM user_balance_record " +
"WHERE status = 1 " +
"ORDER BY create_time DESC " +
"LIMIT 1")
UserBalanceRecordEntity selectFirstSuccessRecord();
@Select("SELECT * " +
"FROM user_balance_record " +
"WHERE status = 1 " +
"ORDER BY create_time DESC")
List<UserBalanceRecordEntity> selectAllSuccessRecords();
@Select("SELECT * FROM user_balance_record WHERE ab98_user_id = #{ab98UserId} LIMIT 1")
UserBalanceRecordEntity selectByAb98UserId(Long ab98UserId);
@Select("SELECT * FROM user_balance_record WHERE trade_id = #{tradeId} LIMIT 1")
UserBalanceRecordEntity selectByTradeId(String tradeId);
}

View File

@ -0,0 +1,28 @@
package com.agileboot.domain.ab98.balance.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 用户余额交易记录表 服务类
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
public interface UserBalanceRecordService extends IService<UserBalanceRecordEntity> {
Page<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query);
List<UserBalanceRecordEntity> selectAllSuccessRecords();
UserBalanceRecordEntity getFirstSuccessRecord();
UserBalanceRecordEntity getByAb98UserId(Long ab98UserId);
UserBalanceRecordEntity getByTradeId(String tradeId);
}

View File

@ -0,0 +1,48 @@
package com.agileboot.domain.ab98.balance.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* 用户余额交易记录表 服务实现类
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
@Service
public class UserBalanceRecordServiceImpl extends ServiceImpl<UserBalanceRecordMapper, UserBalanceRecordEntity> implements UserBalanceRecordService {
@Override
public Page<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query) {
return baseMapper.getUserBalanceRecordList(query.toPage(), query.toQueryWrapper());
}
@Override
public List<UserBalanceRecordEntity> selectAllSuccessRecords() {
return baseMapper.selectAllSuccessRecords();
}
@Override
public UserBalanceRecordEntity getFirstSuccessRecord() {
return baseMapper.selectFirstSuccessRecord();
}
@Override
public UserBalanceRecordEntity getByAb98UserId(Long ab98UserId) {
return baseMapper.selectByAb98UserId(ab98UserId);
}
@Override
public UserBalanceRecordEntity getByTradeId(String tradeId) {
return baseMapper.selectByTradeId(tradeId);
}
}

View File

@ -0,0 +1,99 @@
package com.agileboot.domain.ab98.balance.dto;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import lombok.Data;
import java.util.Date;
@ExcelSheet(name = "用户余额交易记录")
@Data
public class UserBalanceRecordDTO {
public UserBalanceRecordDTO(UserBalanceRecordEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
// 交易类型转换
if (entity.getTransactionType() != null) {
switch (entity.getTransactionType()) {
case 1:
this.transactionTypeStr = "充值";
break;
case 2:
this.transactionTypeStr = "消费";
break;
case 3:
this.transactionTypeStr = "退款";
break;
default:
this.transactionTypeStr = "未知";
}
}
// 交易状态转换
if (entity.getStatus() != null) {
switch (entity.getStatus()) {
case 0:
this.statusStr = "未完成";
break;
case 1:
this.statusStr = "成功";
break;
case 2:
this.statusStr = "失败";
break;
default:
this.statusStr = "未知";
}
}
// 格式化创建时间
if (entity.getCreateTime() != null) {
this.createTimeStr = DateUtil.format(entity.getCreateTime(), "yyyy-MM-dd HH:mm:ss");
}
}
}
@ExcelColumn(name = "主键ID")
private Long balanceRecordId;
@ExcelColumn(name = "汇邦云用户唯一ID")
private Long ab98UserId;
@ExcelColumn(name = "openid")
private String openid;
@ExcelColumn(name = "交易类型")
private String transactionTypeStr;
private Integer transactionType;
@ExcelColumn(name = "关联充值记录ID")
private Long useRecordId;
@ExcelColumn(name = "交易金额(分)")
private Integer amount;
@ExcelColumn(name = "交易状态")
private String statusStr;
private Integer status;
@ExcelColumn(name = "支付网关交易ID")
private String tradeId;
@ExcelColumn(name = "业务系统订单ID")
private String bizOrderId;
@ExcelColumn(name = "剩余余额(分)")
private Integer remainAmount;
@ExcelColumn(name = "创建时间")
private String createTimeStr;
private Date createTime;
}

View File

@ -0,0 +1,41 @@
package com.agileboot.domain.ab98.balance.model;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UserBalanceRecordModel extends UserBalanceRecordEntity {
private UserBalanceRecordService userBalanceRecordService;
public UserBalanceRecordModel(UserBalanceRecordEntity entity, UserBalanceRecordService userBalanceRecordService) {
this(userBalanceRecordService);
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public UserBalanceRecordModel(UserBalanceRecordService userBalanceRecordService) {
this.userBalanceRecordService = userBalanceRecordService;
}
public void loadAddCommand(AddUserBalanceRecordCommand command) {
if (command != null) {
BeanUtil.copyProperties(command, this, "balanceRecordId");
}
}
public void loadUpdateCommand(UpdateUserBalanceRecordCommand command) {
if (command != null) {
loadAddCommand(command);
}
}
}

View File

@ -0,0 +1,27 @@
package com.agileboot.domain.ab98.balance.model;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class UserBalanceRecordModelFactory {
private final UserBalanceRecordService userBalanceRecordService;
public UserBalanceRecordModel loadById(Long balanceRecordId) {
UserBalanceRecordEntity entity = userBalanceRecordService.getById(balanceRecordId);
if (entity == null) {
throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, balanceRecordId, "用户余额交易记录");
}
return new UserBalanceRecordModel(entity, userBalanceRecordService);
}
public UserBalanceRecordModel create() {
return new UserBalanceRecordModel(userBalanceRecordService);
}
}

View File

@ -0,0 +1,41 @@
package com.agileboot.domain.ab98.balance.query;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class SearchUserBalanceRecordQuery<T> extends AbstractPageQuery<T> {
private Long ab98UserId;
private String openid;
private Integer transactionType;
private Integer status;
private String tradeId;
private String bizOrderId;
private Date startTime;
private Date endTime;
@Override
public QueryWrapper<T> addQueryCondition() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(ab98UserId != null, "ab98_user_id", ab98UserId)
.like(StrUtil.isNotEmpty(openid), "openid", openid)
.eq(transactionType != null, "transaction_type", transactionType)
.eq(status != null, "status", status)
.like(StrUtil.isNotEmpty(tradeId), "trade_id", tradeId)
.like(StrUtil.isNotEmpty(bizOrderId), "biz_order_id", bizOrderId)
.between(startTime != null && endTime != null, "create_time", startTime, endTime);
this.timeRangeColumn = "create_time";
return queryWrapper;
}
}

View File

@ -40,7 +40,7 @@ public class Ab98UserEntity extends BaseEntity<Ab98UserEntity> {
private String userid;
@ApiModelProperty("真实姓名")
@TableField("`name`")
@TableField("name")
private String name;
@ApiModelProperty("手机号码")
@ -75,6 +75,10 @@ public class Ab98UserEntity extends BaseEntity<Ab98UserEntity> {
@TableField("registered")
private Boolean registered;
@ApiModelProperty("用户余额(单位:分)")
@TableField("ab98_balance")
private Integer ab98Balance;
@Override
public Serializable pkVal() {

View File

@ -39,10 +39,13 @@ public interface Ab98UserMapper extends BaseMapper<Ab98UserEntity> {
List<Ab98UserEntity> selectAllRegistered();
@Select("SELECT * FROM ab98_user WHERE userid = #{userid} LIMIT 1")
Ab98UserEntity selectByUserid(String userid);
Ab98UserEntity selectByUserid(@Param("userid")String userid);
@Select("SELECT * FROM ab98_user WHERE openid = #{openid} LIMIT 1")
Ab98UserEntity selectByOpenid(String openid);
Ab98UserEntity selectByOpenid(@Param("openid")String openid);
@Select("SELECT * FROM ab98_user WHERE tel = #{tel} LIMIT 1")
Ab98UserEntity selectByTel(@Param("tel")String tel);
@Select("SELECT * FROM ab98_user WHERE openid = #{openid} AND userid = #{userid} LIMIT 1")
Ab98UserEntity selectByOpenidAndUserid(@Param("openid")String openid, @Param("userid")String userid);

View File

@ -28,5 +28,7 @@ public interface Ab98UserService extends IService<Ab98UserEntity> {
Ab98UserEntity getByIdnum(String idnum);
Ab98UserEntity selectByTel(String tel);
Ab98UserEntity selectByOpenidAndUserid(String openid, String userid);
}

View File

@ -49,6 +49,11 @@ public class Ab98UserServiceImpl extends ServiceImpl<Ab98UserMapper, Ab98UserEnt
return this.getOne(wrapper);
}
@Override
public Ab98UserEntity selectByTel(String tel) {
return baseMapper.selectByTel(tel);
}
@Override
public Ab98UserEntity selectByOpenidAndUserid(String openid, String userid) {
return baseMapper.selectByOpenidAndUserid(openid, userid);

View File

@ -58,6 +58,9 @@ public class Ab98UserDTO {
@ExcelColumn(name = "身份证登记地址")
private String address;
@ExcelColumn(name = "用户余额(单位:分)")
private Integer ab98Balance;
@ExcelColumn(name = "注册状态")
private String registeredStatus;

View File

@ -59,6 +59,9 @@ public class Ab98UserDetailDTO extends BaseEntity<Ab98UserDetailDTO> {
@ExcelColumn(name = "身份证登记地址")
private String address;
@ExcelColumn(name = "用户余额(单位:分)")
private Integer ab98Balance;
@ExcelColumn(name = "注册状态")
private String registeredStatus;
}

View File

@ -2,6 +2,7 @@ package com.agileboot.domain.cabinet.cell;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.cabinet.cell.db.CabinetCellDO;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellLatestOrderDTO;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellWithOrderCountDTO;
import com.agileboot.domain.cabinet.cell.query.SearchCabinetCellWithOrdersQuery;
import com.agileboot.domain.common.command.BulkOperationCommand;
@ -20,6 +21,7 @@ import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
@ -33,7 +35,27 @@ public class CabinetCellApplicationService {
public PageDTO<CabinetCellWithOrderCountDTO> getCabinetCellList(SearchCabinetCellWithOrdersQuery<CabinetCellEntity> query) {
Page<CabinetCellWithOrderCountDTO> page = cabinetCellService.getCellListWithOrders(query);
return new PageDTO<>(page.getRecords(), page.getTotal());
List<CabinetCellWithOrderCountDTO> list = page.getRecords();
List<CabinetCellWithOrderCountDTO> rentingCells = list.stream()
.filter(cell -> cell.getIsRented().equals(1))
.collect(Collectors.toList());
if (!rentingCells.isEmpty()) {
List<CabinetCellLatestOrderDTO> cellLatestOrderDTOS = cabinetCellService.selectLatestOrderInfoByCell(rentingCells.stream()
.map(CabinetCellWithOrderCountDTO::getCellId).collect(Collectors.toList()));
for (CabinetCellWithOrderCountDTO cell : rentingCells) {
cellLatestOrderDTOS.stream()
.filter(order -> order.getCellId().equals(cell.getCellId()))
.findFirst()
.ifPresent(order -> {
BeanUtils.copyProperties(order, cell);
});
}
}
return new PageDTO<>(list, page.getTotal());
}
public void addCabinetCell(AddCabinetCellCommand command) {

View File

@ -77,6 +77,14 @@ public class CabinetCellEntity extends BaseEntity<CabinetCellEntity> {
@TableField("goods_id")
private Long goodsId;
@ApiModelProperty("关联订单ID")
@TableField(exist = false)
private Long orderId;
@ApiModelProperty("关联订单商品ID")
@TableField(exist = false)
private Long orderGoodsId;
@Override
public Serializable pkVal() {

View File

@ -1,5 +1,6 @@
package com.agileboot.domain.cabinet.cell.db;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellLatestOrderDTO;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellWithOrderCountDTO;
import com.agileboot.domain.cabinet.smartCabinet.db.SmartCabinetDO;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
@ -74,4 +75,48 @@ public interface CabinetCellMapper extends BaseMapper<CabinetCellEntity> {
"WHERE cabinet_id = #{cabinetId} AND deleted = 0 " +
"ORDER BY cell_id ASC")
List<CabinetCellDO> selectCabinetCellDOList(@Param("cabinetId") Long cabinetId);
@Select("SELECT " +
" cc.cell_id, " +
" sog.order_goods_id, " +
" so.order_id, " +
" so.name, " +
" so.mobile, " +
" au.face_img, " +
" au.ab98_user_id " +
"FROM " +
" cabinet_cell cc " +
"INNER JOIN ( " +
" SELECT " +
" sog_inner.order_goods_id, " +
" sog_inner.order_id, " +
" sog_inner.status, " +
" sog_inner.cell_id " +
" FROM ( " +
" SELECT " +
" sog.*, " +
" ROW_NUMBER() OVER ( " +
" PARTITION BY sog.cell_id " +
" ORDER BY sog.create_time DESC, sog.order_goods_id DESC " +
" ) AS rn " +
" FROM shop_order_goods sog " +
" WHERE " +
" sog.deleted = 0 " +
" AND sog.status = 1 " +
" AND sog.cell_id IS NOT NULL " +
" ) sog_inner " +
" WHERE sog_inner.rn = 1 " +
") sog ON cc.cell_id = sog.cell_id " +
"INNER JOIN shop_order so ON sog.order_id = so.order_id " +
"LEFT JOIN ab98_user au ON so.name = au.name AND so.mobile = au.tel AND au.deleted = 0 " +
"${ew.customSqlSegment} ")
List<CabinetCellLatestOrderDTO> selectLatestOrderInfoByCell(@Param(Constants.WRAPPER) Wrapper<?> queryWrapper);
@Select("SELECT DISTINCT cc.*, sog.order_id as orderId, sog.order_goods_id as orderGoodsId " +
"FROM cabinet_cell cc " +
"INNER JOIN shop_order_goods sog ON cc.cell_id = sog.cell_id AND sog.deleted = 0 " +
"INNER JOIN shop_order so ON sog.order_id = so.order_id AND so.deleted = 0 " +
"INNER JOIN ab98_user au ON so.name = au.name AND so.mobile = au.tel AND au.deleted = 0 " +
"${ew.customSqlSegment}")
List<CabinetCellEntity> selectRentedCellsByAb98UserIdAndCorpid(@Param(Constants.WRAPPER) Wrapper<?> queryWrapper);
}

View File

@ -1,6 +1,7 @@
package com.agileboot.domain.cabinet.cell.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellLatestOrderDTO;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellWithOrderCountDTO;
import com.agileboot.domain.cabinet.smartCabinet.dto.CabinetDetailDTO;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
@ -37,4 +38,8 @@ public interface CabinetCellService extends IService<CabinetCellEntity> {
Long countLinkedRecord();
List<CabinetCellDO> selectCabinetCellDOList(Long cabinetId);
List<CabinetCellLatestOrderDTO> selectLatestOrderInfoByCell(List<Long> cellIds);
List<CabinetCellEntity> selectRentedCellsByAb98UserIdAndCorpid(Long ab98UserId, String corpid);
}

View File

@ -1,9 +1,11 @@
package com.agileboot.domain.cabinet.cell.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellLatestOrderDTO;
import com.agileboot.domain.cabinet.cell.dto.CabinetCellWithOrderCountDTO;
import com.agileboot.domain.cabinet.smartCabinet.dto.CabinetDetailDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
@ -61,4 +63,32 @@ public class CabinetCellServiceImpl extends ServiceImpl<CabinetCellMapper, Cabin
public List<CabinetCellDO> selectCabinetCellDOList(Long cabinetId) {
return baseMapper.selectCabinetCellDOList(cabinetId);
}
@Override
public List<CabinetCellLatestOrderDTO> selectLatestOrderInfoByCell(List<Long> cellIds) {
QueryWrapper<CabinetCellEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cc.is_rented", 1)
.eq("cc.deleted", 0)
.eq("so.deleted", 0)
.eq("so.status", 2)
.eq("so.pay_status", 2)
.eq("sog.status", 1)
.in("cc.cell_id", cellIds);
return baseMapper.selectLatestOrderInfoByCell(queryWrapper);
}
@Override
public List<CabinetCellEntity> selectRentedCellsByAb98UserIdAndCorpid(Long ab98UserId, String corpid) {
QueryWrapper<CabinetCellEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("cc.is_rented", 1)
.eq("cc.deleted", 0)
.eq("so.deleted", 0)
.eq("sog.deleted", 0)
.eq("sog.mode", 3)
.eq("sog.status", 1)
.eq("au.deleted", 0)
.eq("so.corpid", corpid)
.eq("au.ab98_user_id", ab98UserId);
return baseMapper.selectRentedCellsByAb98UserIdAndCorpid(queryWrapper);
}
}

View File

@ -0,0 +1,44 @@
package com.agileboot.domain.cabinet.cell.dto;
import lombok.Data;
/**
* 柜机格口最新订单信息DTO
*/
@Data
public class CabinetCellLatestOrderDTO {
/**
* 格口ID
*/
private Long cellId;
/**
* 订单商品ID
*/
private Long orderGoodsId;
/**
* 订单ID
*/
private Long orderId;
/**
* 客户姓名
*/
private String name;
/**
* 客户手机号
*/
private String mobile;
/**
* 人脸照片地址
*/
private String faceImg;
/**
* 汇邦云用户ID
*/
private Long ab98UserId;
}

View File

@ -25,4 +25,38 @@ public class CabinetCellWithOrderCountDTO extends CabinetCellEntity {
@ApiModelProperty("关联商品图片")
@TableField("cover_img")
private String coverImg;
/**
* 订单商品ID
*/
@ApiModelProperty("订单商品ID")
private Long orderGoodsId;
/**
* 订单ID
*/
@ApiModelProperty("订单ID")
private Long orderId;
/**
* 客户姓名
*/
@ApiModelProperty("客户姓名")
private String name;
/**
* 客户手机号
*/
@ApiModelProperty("客户手机号")
private String mobile;
/**
* 人脸照片地址
*/
@ApiModelProperty("人脸照片地址")
private String faceImg;
/**
* 汇邦云用户ID
*/
@ApiModelProperty("汇邦云用户ID")
private Long ab98UserId;
}

View File

@ -323,4 +323,46 @@ public class SmartCabinetApplicationService {
public List<SmartCabinetDO> getConsumablesCabinetList() {
return smartCabinetService.selectSmartCabinetDOList();
}
public List<CabinetDetailDTO> getUserRentedCabinetList(String corpid, Long ab98UserId) {
List<CabinetCellEntity> cabinetCells = cabinetCellService.selectRentedCellsByAb98UserIdAndCorpid(ab98UserId, corpid);
// 获取所有智能柜单元格和商品的基础数据
QueryWrapper<SmartCabinetEntity> smartCabinetEntityQueryWrapper = new QueryWrapper<>();
smartCabinetEntityQueryWrapper.in("cabinet_id", cabinetCells.stream()
.map(CabinetCellEntity::getCabinetId).filter(Objects::nonNull).distinct().collect(Collectors.toList()))
.eq("deleted", false);
List<SmartCabinetEntity> smartCabinets = smartCabinetService.list(smartCabinetEntityQueryWrapper);
List<CabinetDetailDTO> result = new ArrayList<>();
// 遍历每个智能柜构建详细信息
for (SmartCabinetEntity cabinet : smartCabinets) {
CabinetDetailDTO cabinetDetailDTO = new CabinetDetailDTO();
// 设置柜体基础信息
cabinetDetailDTO.setCabinetId(cabinet.getCabinetId());
cabinetDetailDTO.setCabinetName(cabinet.getCabinetName());
cabinetDetailDTO.setLockControlNo(cabinet.getLockControlNo());
// 处理柜体下的所有单元格
List<CabinetDetailDTO.CellInfoDTO> cellInfoList = cabinetCells.stream()
.filter(cell -> cell.getCabinetId().equals(cabinet.getCabinetId()))
.map(cell -> {
CabinetDetailDTO.CellInfoDTO cellInfo = new CabinetDetailDTO.CellInfoDTO();
// 设置单元格基础信息
cellInfo.setCellId(cell.getCellId());
cellInfo.setCellNo(cell.getCellNo());
cellInfo.setPinNo(cell.getPinNo());
cellInfo.setStock(cell.getStock());
cellInfo.setCellPrice(cell.getCellPrice());
cellInfo.setOrderId(cell.getOrderId());
cellInfo.setOrderGoodsId(cell.getOrderGoodsId());
return cellInfo;
}).collect(Collectors.toList());
// 将单元格列表加入柜体信息
cabinetDetailDTO.setCells(cellInfoList);
result.add(cabinetDetailDTO);
}
return result;
}
}

View File

@ -79,6 +79,10 @@ public class SmartCabinetEntity extends BaseEntity<SmartCabinetEntity> {
@TableField("location")
private Integer location;
@ApiModelProperty("归还期限0表示不限制")
@TableField("return_deadline")
private Integer returnDeadline;
@ApiModelProperty("已用格口数")
@TableField(exist = false)
private Integer usedCells;

View File

@ -18,6 +18,9 @@ public class CabinetDetailDTO {
private Integer cellNo;
private Integer pinNo;
private Integer stock;
private BigDecimal cellPrice;
private Long orderId;
private Long orderGoodsId;
private ProductInfoDTO product;
}

View File

@ -62,6 +62,9 @@ public class SmartCabinetDTO {
@ExcelColumn(name = "柜机位置")
private Integer location;
@ExcelColumn(name = "归还期限0表示不限制")
private Integer returnDeadline;
@ExcelColumn(name = "已用格口数")
private Integer usedCells;

View File

@ -44,7 +44,7 @@ public interface SysUserQyUserMapper extends BaseMapper<SysUserQyUserEntity> {
"FROM sys_user_qy_user suqy " +
"JOIN qy_user qu ON suqy.qy_user_id = qu.id " +
"JOIN sys_user su ON suqy.sys_user_id = su.user_id " +
"WHERE qu.userid = #{userid}")
"WHERE qu.userid = #{userid} AND qu.deleted = 0 AND qu.enable = 1")
SysUserEntity selectSysUserByQyUserid(@Param("userid") String userid);
}

View File

@ -443,8 +443,11 @@ public class ReturnApprovalApplicationService {
* 执行更新审批状态为通过以及之后的操作
*/
public void updateApprovalStatusAndComplete(UpdateReturnApprovalCommand command) {
long startTime = System.currentTimeMillis();
// 加载退货审批模型
ReturnApprovalModel model = modelFactory.loadById(command.getApprovalId());
long modelLoadTime = System.currentTimeMillis();
// 设置审批人信息
if (StringUtils.isNotBlank(command.getAuditName())) {
@ -455,13 +458,17 @@ public class ReturnApprovalApplicationService {
model.setAuditName(qyUserEntity.getName());
}
}
long auditInfoTime = System.currentTimeMillis();
// 获取关联的订单商品列表
List<ApprovalGoodsEntity> approvalGoodsList = approvalGoodsService.selectByApprovalId(model.getApprovalId());
if (approvalGoodsList == null || approvalGoodsList.isEmpty()) {
throw new RuntimeException("未找到关联的订单商品");
}
long goodsListTime = System.currentTimeMillis();
List<ApprovalGoodsCellEntity> approvalGoodsCellEntities = approvalGoodsCellService.selectByApprovalId(model.getApprovalId());
long goodsCellTime = System.currentTimeMillis();
// 获取关联的商品列表
QueryWrapper<ShopGoodsEntity> goodsWrapper = new QueryWrapper<>();
@ -471,12 +478,14 @@ public class ReturnApprovalApplicationService {
if (goodsList == null || goodsList.isEmpty()) {
throw new RuntimeException("未找到关联的商品");
}
long shopGoodsTime = System.currentTimeMillis();
// 初始化商品和格口模型列表
QueryWrapper<CabinetCellEntity> cellEntityQueryWrapper = new QueryWrapper<>();
cellEntityQueryWrapper.in("cell_id", approvalGoodsCellEntities.stream().map(ApprovalGoodsCellEntity::getCellId).collect(Collectors.toList()))
.eq("deleted", false);
List<CabinetCellEntity> cabinetCellList = cabinetCellService.list(cellEntityQueryWrapper);
long cabinetCellTime = System.currentTimeMillis();
for (ApprovalGoodsCellEntity approvalGoodsCell : approvalGoodsCellEntities) {
if (approvalGoodsCell.getAllocateQuantity() == null || approvalGoodsCell.getAllocateQuantity().compareTo(0) <= 0) {
@ -515,10 +524,14 @@ public class ReturnApprovalApplicationService {
model.setAuditUserid(command.getAuditUserid());
model.setApprovalTime(new Date());
long stockTime = System.currentTimeMillis();
// 调用固资通服务的出库方法
assetApplicationService.consumeOutput(model.getCorpid(), model.getApplyUserid(), model.getAuditUserid(),
model, approvalGoodsList, goodsList);
long consumeOutputTime = System.currentTimeMillis();
// 开始执行数据库操作
model.setStatus(2); // 2表示审核通过状态
model.updateById();
@ -528,6 +541,28 @@ public class ReturnApprovalApplicationService {
// 批量更新格口库存
cabinetCellList.forEach(CabinetCellEntity::updateById);
long updateDatabaseTime = System.currentTimeMillis();
try {
AddPaymentOperationLogCommand paymentOperationLogCommand = new AddPaymentOperationLogCommand();
paymentOperationLogCommand.setOperationType("assetApprovalTime");
paymentOperationLogCommand.setStatus(1);
paymentOperationLogCommand.setRemark(String.format("Method execution times - Model load: %dms, Audit info: %dms, Goods list: %dms, Goods cell: %dms, Shop goods: %dms, Cabinet cell: %dms, Stock: %dms, Consume output: %dms, Update database: %dms",
modelLoadTime - startTime,
auditInfoTime - modelLoadTime,
goodsListTime - auditInfoTime,
goodsCellTime - goodsListTime,
shopGoodsTime - goodsCellTime,
cabinetCellTime - shopGoodsTime,
stockTime - cabinetCellTime,
consumeOutputTime - stockTime,
updateDatabaseTime - consumeOutputTime));
paymentOperationLogCommand.initBaseEntity();
paymentOperationLogApplicationService.addPaymentOperationLog(paymentOperationLogCommand);
} catch (Exception e) {
log.error("记录操作日志失败", e);
}
}

View File

@ -54,10 +54,7 @@ import com.agileboot.domain.shop.paymentOperationLog.command.AddPaymentOperation
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -413,18 +410,19 @@ public class OrderApplicationService {
* @param corpid 企业微信 ID
* @return 包含订单列表订单商品列表和商品列表的 DTO 对象
*/
public GetOrdersByOpenIdDTO getOrdersByOpenId(String openid, String corpid) {
public GetOrdersByOpenIdDTO getOrdersByOpenId(String openid, String corpid, Integer hasReturn) {
// 根据 openid 获取 Ab98 用户信息
Ab98UserEntity ab98User = ab98UserService.getByOpenid(openid);
// 构建订单查询条件
QueryWrapper<ShopOrderEntity> orderQueryWrapper = new QueryWrapper<>();
// 添加 openid 作为查询条件
orderQueryWrapper.eq("openid", openid);
orderQueryWrapper.and(wrapper -> {
wrapper.eq("openid", openid);
// 如果 Ab98 用户信息存在
if (ab98User != null) {
// 添加 or 条件根据 Ab98 用户的 userid 查询订单
orderQueryWrapper.or()
wrapper.or()
.eq("userid", ab98User.getUserid());
// 如果 corpid 不为空
@ -434,21 +432,33 @@ public class OrderApplicationService {
// 如果企业微信用户信息存在
if (qyUser!= null) {
// 添加 or 条件根据企业微信用户的 userid 查询订单
orderQueryWrapper.or()
wrapper.or()
.eq("userid", qyUser.getUserid());
}
}
}
});
// 根据查询条件获取订单列表
orderQueryWrapper.eq("pay_status", 2);
List<ShopOrderEntity> orderList = orderService.list(orderQueryWrapper);
// 构建订单商品查询条件
QueryWrapper<ShopOrderGoodsEntity> orderGoodsQueryWrapper = new QueryWrapper<>();
// 添加订单 ID 作为查询条件查询属于这些订单的商品
orderGoodsQueryWrapper.in("order_id", orderList.stream().map(ShopOrderEntity::getOrderId).collect(Collectors.toList()));
if (Integer.valueOf(1).equals(hasReturn)) {
orderGoodsQueryWrapper.eq("status", 2);
} else if (Integer.valueOf(0).equals(hasReturn)) {
orderGoodsQueryWrapper.ne("status", 2);
}
// 根据查询条件获取订单商品列表
List<ShopOrderGoodsEntity> orderGoods = orderGoodsService.list(orderGoodsQueryWrapper);
Set<Long> orderIds = orderGoods.stream().map(ShopOrderGoodsEntity::getOrderId).collect(Collectors.toSet());
orderList = orderList.stream()
.filter(order -> orderIds.contains(order.getOrderId()))
.collect(Collectors.toList());
// 构建商品查询条件
QueryWrapper<ShopGoodsEntity> goodsQueryWrapper = new QueryWrapper<>();
// 添加商品 ID 作为查询条件查询这些商品信息
@ -466,7 +476,7 @@ public class OrderApplicationService {
* @param qyUserId 企业微信用户 ID
* @return 包含订单列表订单商品列表和商品列表的 DTO 对象
*/
public GetOrdersByOpenIdDTO getOrdersByQyUserId(Long qyUserId) {
public GetOrdersByOpenIdDTO getOrdersByQyUserId(Long qyUserId, Integer hasReturn) {
// 校验 qyUserId 是否为空为空则抛出异常
if (qyUserId == null) {
throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "qyUserId不能为空");
@ -477,7 +487,8 @@ public class OrderApplicationService {
// 构建订单查询条件
QueryWrapper<ShopOrderEntity> orderQueryWrapper = new QueryWrapper<>();
// 添加企业微信用户的 userid 作为查询条件
orderQueryWrapper.eq("userid", qyUser.getUserid());
orderQueryWrapper.and(wrapper -> {
wrapper.eq("userid", qyUser.getUserid());
// 如果企业微信用户的 ab98UserId 不为空
if (qyUser.getAb98UserId() != null) {
// 根据 ab98UserId 获取 Ab98 用户信息
@ -485,18 +496,20 @@ public class OrderApplicationService {
// 如果 Ab98 用户信息存在
if (ab98User!= null) {
// 添加 or 条件根据 Ab98 用户的 userid 查询订单
orderQueryWrapper.or()
wrapper.or()
.eq("userid", ab98User.getUserid());
// 如果 Ab98 用户的 openid 不为空
if (StringUtils.isNotBlank(ab98User.getOpenid())) {
// 添加 or 条件根据 Ab98 用户的 openid 查询订单
orderQueryWrapper.or()
wrapper.or()
.eq("openid", ab98User.getOpenid());
}
}
}
});
// 根据查询条件获取订单列表
orderQueryWrapper.eq("pay_status", 2);
List<ShopOrderEntity> orderList = orderService.list(orderQueryWrapper);
// 构建订单商品查询条件
@ -508,11 +521,24 @@ public class OrderApplicationService {
// 添加订单 ID 作为查询条件查询属于这些订单的商品
QueryWrapper<ShopOrderGoodsEntity> orderGoodsQueryWrapper = new QueryWrapper<>();
orderGoodsQueryWrapper.in("order_id", orderList.stream().map(ShopOrderEntity::getOrderId).collect(Collectors.toList()));
if (Integer.valueOf(1).equals(hasReturn)) {
orderGoodsQueryWrapper.eq("status", 2);
} else if (Integer.valueOf(0).equals(hasReturn)) {
orderGoodsQueryWrapper.ne("status", 2);
}
List<ShopOrderGoodsEntity> orderGoods = orderGoodsService.list(orderGoodsQueryWrapper);
Set<Long> orderIds = orderGoods.stream().map(ShopOrderGoodsEntity::getOrderId).collect(Collectors.toSet());
orderList = orderList.stream()
.filter(order -> orderIds.contains(order.getOrderId()))
.collect(Collectors.toList());
List<ShopGoodsEntity> goods = new ArrayList<>();
if (!orderIds.isEmpty()) {
QueryWrapper<ShopGoodsEntity> goodsQueryWrapper = new QueryWrapper<>();
goodsQueryWrapper.in("goods_id", orderGoods.stream().map(ShopOrderGoodsEntity::getGoodsId).collect(Collectors.toList()));
List<ShopGoodsEntity> goods = goodsService.list(goodsQueryWrapper);
goods = goodsService.list(goodsQueryWrapper);
}
return new GetOrdersByOpenIdDTO(orderList, orderGoods, goods);
}
@ -545,7 +571,7 @@ public class OrderApplicationService {
return orderService.sumTotalAmount();
}
public List<ShopOrderGoodsEntity> selectUnReturnOrderGoods() {
return orderGoodsService.selectUnReturnOrderGoods();
public List<ShopOrderGoodsEntity> selectUnReturnOrderGoods(String corpid) {
return orderGoodsService.selectUnReturnOrderGoods(corpid);
}
}

View File

@ -26,6 +26,14 @@ import lombok.Setter;
@ApiModel(value = "ShopOrderGoodsEntity对象", description = "订单商品明细表")
public class ShopOrderGoodsEntity extends BaseEntity<ShopOrderGoodsEntity> {
public static final Integer STATUS_NORMAL_BORROWED = 1;
public static final Integer STATUS_RETURNED = 2;
public static final Integer STATUS_EXCHANGED = 3;
public static final Integer STATUS_COMPLETED = 4;
public static final Integer STATUS_UNDER_REVIEW = 5;
public static final Integer STATUS_RETURN_REJECTED = 6;
public static final Integer STATUS_OVERDUE = 7;
private static final long serialVersionUID = 1L;
@ApiModelProperty("订单商品唯一ID")
@ -68,7 +76,7 @@ public class ShopOrderGoodsEntity extends BaseEntity<ShopOrderGoodsEntity> {
@TableField("cover_img")
private String coverImg;
@ApiModelProperty("商品状态1正常 2已退货 3已换货 4已完成 5审核中 6退货未通过")
@ApiModelProperty("商品状态1正常/借出中 2已退货 3已换货 4已完成 5审核中 6退货未通过 7已逾期")
@TableField("status")
private Integer status;

View File

@ -46,9 +46,9 @@ public interface ShopOrderGoodsMapper extends BaseMapper<ShopOrderGoodsEntity> {
"FROM shop_order_goods og " +
"LEFT JOIN shop_goods g ON og.goods_id = g.goods_id " +
"LEFT JOIN cabinet_cell cc ON og.cell_id = cc.cell_id " +
"WHERE og.status = 1 " +
"WHERE og.corpid = #{corpid} AND og.status = 1 " +
"AND og.deleted = 0 " +
"AND g.deleted = 0 " +
"AND cc.deleted = 0 AND cc.goods_id = og.goods_id")
List<ShopOrderGoodsEntity> selectUnReturnOrderGoods();
List<ShopOrderGoodsEntity> selectUnReturnOrderGoods(@Param("corpid") String corpid);
}

View File

@ -23,7 +23,7 @@ public interface ShopOrderGoodsService extends IService<ShopOrderGoodsEntity> {
List<TodayLatestOrderGoodsDTO> selectTodayLatestOrderGoods();
List<ShopOrderGoodsEntity> selectUnReturnOrderGoods();
List<ShopOrderGoodsEntity> selectUnReturnOrderGoods(String corpid);
List<ShopOrderGoodsEntity> selectOrderGoodsByApprovalId(Long approvalId);
}

View File

@ -39,8 +39,8 @@ public class ShopOrderGoodsServiceImpl extends ServiceImpl<ShopOrderGoodsMapper,
}
@Override
public List<ShopOrderGoodsEntity> selectUnReturnOrderGoods() {
return baseMapper.selectUnReturnOrderGoods();
public List<ShopOrderGoodsEntity> selectUnReturnOrderGoods(String corpid) {
return baseMapper.selectUnReturnOrderGoods(corpid);
}
@Override

View File

@ -22,7 +22,8 @@ public interface ShopOrderMapper extends BaseMapper<ShopOrderEntity> {
@Select("SELECT o.*, " +
"GROUP_CONCAT(DISTINCT og.goods_name) AS goodsNames, " +
"GROUP_CONCAT(DISTINCT og.cover_img) AS coverImgs " +
"GROUP_CONCAT(DISTINCT og.cover_img) AS coverImgs, " +
"CASE WHEN o.status = 2 AND MAX(CASE WHEN og.status = 2 THEN 0 ELSE 1 END) = 1 THEN 0 ELSE 1 END AS returnStatus " +
"FROM shop_order o " +
"LEFT JOIN shop_order_goods og ON o.order_id = og.order_id AND og.deleted = 0 " +
"LEFT JOIN cabinet_cell cc ON cc.cell_id = og.cell_id " +

View File

@ -9,4 +9,6 @@ import lombok.EqualsAndHashCode;
public class OrderWithGoodsDTO extends ShopOrderEntity {
private String goodsNames;
private String coverImgs;
// 0未退还 1已退还
private Integer returnStatus;
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.agileboot.domain.ab98.balance.db.UserBalanceRecordMapper">
</mapper>

View File

@ -61,7 +61,7 @@ public class CodeGenerator {
//生成的类 放在orm子模块下的/target/generated-code目录底下
.module("/agileboot-orm/target/generated-code")
.parentPackage("com.agileboot")
.tableName("approval_goods_cell")
.tableName("user_balance_record")
// 决定是否继承基类
.isExtendsFromBaseEntity(true)
.build();

24
sql/20250703.sql Normal file
View File

@ -0,0 +1,24 @@
CREATE TABLE `user_balance_record` (
`balance_record_id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`ab98_user_id` bigint NOT NULL COMMENT '汇邦云用户唯一ID',
`openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'openid',
`transaction_type` tinyint NOT NULL COMMENT '交易类型1充值 2消费 3退款',
`use_record_id` bigint NULL COMMENT '消费或退款时使用的充值id',
`amount` int NOT NULL COMMENT '交易金额,单位分',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '交易状态0未完成 1成功 2失败',
`trade_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '支付网关交易id',
`biz_order_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务系统订单ID对接外部系统',
`creator_id` bigint DEFAULT 0 COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint DEFAULT 0 COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`balance_record_id`),
KEY `idx_ab98_user_id` (`ab98_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户余额交易记录表';
ALTER TABLE `ab98_user`
ADD COLUMN `ab98_balance` INT NOT NULL DEFAULT 0 COMMENT '用户余额(单位:分)';
ALTER TABLE `user_balance_record`
ADD COLUMN `remain_amount` INT NOT NULL DEFAULT 0 COMMENT '本次充值剩余余额(单位:分)';

1
sql/20250718.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE `smart_cabinet` ADD COLUMN `return_deadline` INT NOT NULL DEFAULT 0 COMMENT '归还期限0表示不限制';