记得上下班打卡 | git大法好,push需谨慎

Commit 8869f038 authored by 姜秀龙's avatar 姜秀龙

Merge remote-tracking branch 'refs/remotes/origin/master' into jxl-card

parents 4cbd21cc e1834f3c
-- 动态表单报名活动 - 字典配置
-- dict_value:英文活动标识,与 C 端提交 activityName、库表 sweet_form_submission.activity_name 一致
-- dict_label:后台展示中文名
-- 可按实际活动增删下方字典数据;重复执行不会重复插入(按 dict_type / dict_value 判重)
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
SELECT '动态表单报名活动', 'sweet_form_activity', '0', 'admin', NOW(), '动态表单报名活动标识与中文名映射'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'sweet_form_activity');
-- 示例字典数据(请按实际活动修改或追加)
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
SELECT 1, '2026春季达人招募', 'talent_2026_spring', 'sweet_form_activity', NULL, NULL, 'N', '0', 'admin', NOW(), '示例'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'sweet_form_activity' AND dict_value = 'talent_2026_spring');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
SELECT 2, '音乐节志愿者报名', 'music_fest_volunteer', 'sweet_form_activity', NULL, NULL, 'N', '0', 'admin', NOW(), '示例'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'sweet_form_activity' AND dict_value = 'music_fest_volunteer');
-- 动态表单报名活动管理 - 菜单与权限
-- 挂载在「正在映画」顶级菜单下(按 menu_name 查询,兼容线上/测试 menu_id 不一致)
SET @zhengzaiRootId = (
SELECT menu_id
FROM sys_menu
WHERE menu_name = '正在映画'
AND parent_id = 0
AND menu_type = 'M'
LIMIT 1
);
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('报名活动管理', @zhengzaiRootId, 10, '#', 'menuItem', 'M', '0', NULL, 'fa fa-list-alt', 'admin', NOW(), '', NULL, '动态表单报名活动管理');
SET @parentId = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('活动列表', @parentId, 1, '/sweet/formSubmission/listView', 'menuItem', 'C', '0', 'sweet:formSubmission:list', '#', 'admin', NOW(), '', NULL, '报名活动列表');
SET @menuId = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('导出', @menuId, 1, '#', '', 'F', '0', 'sweet:formSubmission:export', '#', 'admin', NOW(), '', NULL, '');
......@@ -455,6 +455,12 @@ public class GoblinRedisConst {
*/
public static final String SQB_GOBLIN_ORDER_SN_KEY = PREFIX.concat("sqb:orderSn:");
/**
* 订单退款分布式锁
* {goblin:refund:lock:${orderId}, 1}
*/
public static final String REFUND_ORDER_LOCK = PREFIX.concat("refund:lock:");
/**
* 收钱吧 用户订单列表
*/
......
......@@ -83,6 +83,8 @@ public class GoblinBackOrderVo implements Serializable, Cloneable {
private String auditAt;
@ApiModelProperty(value = "创建时间")
private String createdAt;
@ApiModelProperty(value = "发起方[1-用户|2-商家]")
private Integer operationType;
@ApiModelProperty(value = "过期时间")
private String expireAt;
......
package com.liquidnet.service.goblin.dto.vo;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 店铺退款列表接口返回体:分页数据 + 各审核状态 Tab 数量。
*/
@ApiModel("店铺退款列表分页结果")
@Data
public class GoblinStoreBackOrderListPageVo {
/** 当前页列表及分页信息 */
@ApiModelProperty("分页数据")
private PageInfo<GoblinStoreBackOrderListVo> pageInfo;
/** 顶部审核 Tab 角标数量,不受当前 status 筛选影响 */
@ApiModelProperty("各审核状态数量")
private GoblinStoreBackOrderStatusCountVo statusCount;
}
......@@ -20,11 +20,23 @@ public class GoblinStoreBackOrderListVo implements Cloneable {
private Integer type;
@ApiModelProperty(value = "退款/退货状态[0-商铺发起退款|1-退款申请(用户发送退款请求)|2-退款成功(商家同意退款)|3-退款拒绝(商家拒绝退款)|4-退货申请(用户发起退货请求)|5-退货拒绝(商家拒绝退货)|6-退货审核通过等待用户填写物流(商家审核通过,等待用户寄回商品)|7-待收货(用户已确认)|8-退货完成(商家收货并且同意退款给用户)|9-退货失败(商家不同意退款)|10-退款失败|11-取消退款")
private Integer status;
@ApiModelProperty(value = " 退款金额")
@ApiModelProperty(value = "退款范围[1-整单退款|2-部分退款]")
private Integer refundScope;
@ApiModelProperty(value = "原订单实付金额")
private BigDecimal priceActual;
@ApiModelProperty(value = "实际退款金额")
private BigDecimal realBackPrice;
@ApiModelProperty(value = " 创建时间")
@ApiModelProperty(value = "发起方[1-用户发起|2-商家发起]")
private Integer operationType;
@ApiModelProperty(value = "申请时间")
private String createdAt;
@ApiModelProperty(value = "退款sku")
@ApiModelProperty(value = "审核时间")
private String auditAt;
@ApiModelProperty(value = "退款完成时间")
private String refundAt;
@ApiModelProperty(value = "处理时间(优先退款完成时间,其次审核时间,最后拒绝时间)")
private String processedAt;
@ApiModelProperty(value = "退款商品明细")
private List<GoblinBackOrderSkuVo> backOrderSkuVos;
......
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
* 店铺退款列表 - 各 status 数量。
* status 含义见 {@link GoblinBackOrderVo#getStatus()},前端自行组合 Tab。
*/
@ApiModel("店铺退款列表-各审核状态数量")
@Data
public class GoblinStoreBackOrderStatusCountVo {
@ApiModelProperty("全部")
private long all;
@ApiModelProperty("商铺发起退款[status=0]")
private long storeInitiated;
@ApiModelProperty("退款申请[status=1]")
private long refundApply;
@ApiModelProperty("退款成功[status=2]")
private long refundSuccess;
@ApiModelProperty("退款拒绝[status=3]")
private long refundReject;
@ApiModelProperty("退货申请[status=4]")
private long returnApply;
@ApiModelProperty("退货拒绝[status=5]")
private long returnReject;
@ApiModelProperty("退货审核通过待填物流[status=6]")
private long returnWaitLogistics;
@ApiModelProperty("待收货[status=7]")
private long waitReceive;
@ApiModelProperty("退货完成[status=8]")
private long returnCompleted;
@ApiModelProperty("退货失败[status=9]")
private long returnFailed;
@ApiModelProperty("退款失败[status=10]")
private long refundFailed;
@ApiModelProperty("取消退款[status=11]")
private long refundCancelled;
/**
* 从 Mongo 按 status 分组统计结果组装 VO。
*/
public static GoblinStoreBackOrderStatusCountVo of(long all, Map<Integer, Long> grouped) {
GoblinStoreBackOrderStatusCountVo vo = new GoblinStoreBackOrderStatusCountVo();
vo.all = all;
vo.storeInitiated = grouped.getOrDefault(0, 0L);
vo.refundApply = grouped.getOrDefault(1, 0L);
vo.refundSuccess = grouped.getOrDefault(2, 0L);
vo.refundReject = grouped.getOrDefault(3, 0L);
vo.returnApply = grouped.getOrDefault(4, 0L);
vo.returnReject = grouped.getOrDefault(5, 0L);
vo.returnWaitLogistics = grouped.getOrDefault(6, 0L);
vo.waitReceive = grouped.getOrDefault(7, 0L);
vo.returnCompleted = grouped.getOrDefault(8, 0L);
vo.returnFailed = grouped.getOrDefault(9, 0L);
vo.refundFailed = grouped.getOrDefault(10, 0L);
vo.refundCancelled = grouped.getOrDefault(11, 0L);
return vo;
}
}
package com.liquidnet.service.goblin.dto.vo;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 店铺订单列表接口返回体:分页数据 + 各状态 Tab 数量。
*/
@ApiModel("店铺订单列表分页结果")
@Data
public class GoblinStoreOrderListPageVo {
/** 当前页列表及分页信息 */
@ApiModelProperty("分页数据")
private PageInfo<GoblinStoreOrderListVo> pageInfo;
/** 顶部状态 Tab 角标数量,不受当前 status 筛选影响 */
@ApiModelProperty("各状态数量")
private GoblinStoreOrderStatusCountVo statusCount;
}
......@@ -64,6 +64,15 @@ public class GoblinStoreOrderListVo implements Cloneable {
@ApiModelProperty(value = " sku相关")
private List<GoblinStoreOrderListSkuVo> storeOrderListSkuVoList;
@ApiModelProperty(value = " 下单账号-UID")
private String userId;
@ApiModelProperty(value = " 下单账号-昵称")
private String userName;
@ApiModelProperty(value = " 下单账号-手机号")
private String userMobile;
private static final GoblinStoreOrderListVo obj = new GoblinStoreOrderListVo();
......
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
* 店铺订单列表 - 各状态 Tab 数量。
* 对应前端 statusTabs:全部 / 待支付(0) / 待发货(2) / 已发货(3) / 已完成(4) / 已退款(6)
*/
@ApiModel("店铺订单列表-各状态数量")
@Data
public class GoblinStoreOrderStatusCountVo {
/** 全部(含其他 status,如取消单等) */
@ApiModelProperty("全部")
private long all;
/** 待支付 status=0 */
@ApiModelProperty("待支付[status=0]")
private long pendingPay;
/** 待发货 status=2 */
@ApiModelProperty("待发货[status=2]")
private long pendingShip;
/** 已发货 status=3 */
@ApiModelProperty("已发货[status=3]")
private long shipped;
/** 已完成 status=4 */
@ApiModelProperty("已完成[status=4]")
private long completed;
/** 已退款 status=6 */
@ApiModelProperty("已退款[status=6]")
private long refunded;
/**
* 从 Mongo 按 status 分组统计结果组装 VO。
* 新增 Tab 时在此补充 getOrDefault 即可。
*/
public static GoblinStoreOrderStatusCountVo of(long all, Map<Integer, Long> grouped) {
GoblinStoreOrderStatusCountVo vo = new GoblinStoreOrderStatusCountVo();
vo.all = all;
vo.pendingPay = grouped.getOrDefault(0, 0L);
vo.pendingShip = grouped.getOrDefault(2, 0L);
vo.shipped = grouped.getOrDefault(3, 0L);
vo.completed = grouped.getOrDefault(4, 0L);
vo.refunded = grouped.getOrDefault(6, 0L);
return vo;
}
}
package com.liquidnet.service.goblin.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class GoblinStoreOrderRefundParam {
@ApiModelProperty(value = "订单id")
@NotNull(message = "订单id不能为空")
private String orderId;
@ApiModelProperty(value = "退款orderSkuId列表[发货后可选;不传为整单退款;发货前强制整单退款]")
private List<String> orderSkuIds;
@ApiModelProperty(value = "退款原因")
private String reason;
@ApiModelProperty(value = "详细描述")
private String describes;
}
package com.liquidnet.service.goblin.service.manage;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import com.liquidnet.service.goblin.param.GoblinStoreOrderRefundParam;
import java.math.BigDecimal;
public interface IGoblinStoreBackOrderService {
ResponseDto<PageInfo<GoblinStoreBackOrderListVo>> orderBackList(Integer page,
ResponseDto<GoblinStoreBackOrderListPageVo> orderBackList(Integer page,
String orderBackCode,
Integer type,
String cst,
String cet,
String orderCode,
String spuName,
Integer status);
String status);
ResponseDto<GoblinBackOrderDetailsVo> orderDetails(String backOrderId);
......@@ -28,5 +26,7 @@ public interface IGoblinStoreBackOrderService {
ResponseDto<Boolean> refusedRefund(String backOrderId);
ResponseDto<Boolean> refundOrder(GoblinStoreOrderRefundParam param);
ResponseDto<Boolean> changeSkuRefund(String backOrderId, BigDecimal refundPrice, String orderSkuId);
}
package com.liquidnet.service.goblin.service.manage;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinOrderLogVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
public interface IGoblinStoreOrderService {
ResponseDto<PageInfo<GoblinStoreOrderListVo>> orderList(Integer page,
ResponseDto<GoblinStoreOrderListPageVo> orderList(Integer page,
String orderCode,
Integer type,
String cst,
......@@ -20,6 +17,14 @@ public interface IGoblinStoreOrderService {
String phone,
Integer status);
void exportOrderList(HttpServletResponse response,
String orderCode,
String cst,
String cet,
String expressContacts,
String phone,
Integer status);
ResponseDto<GoblinStoreOrderListVo> orderDetails(String orderId);
ResponseDto<Boolean> orderCancel(String orderId);
......@@ -32,8 +37,8 @@ public interface IGoblinStoreOrderService {
ResponseDto<Boolean> refundOrderSku(String orderId, String orderSkuId, BigDecimal price);
ResponseDto<Boolean> express(String orderId, String orderSkuIds,String mailNo,String uid,String orderCode);
ResponseDto<Boolean> express(String orderId, String orderSkuIds, String mailNo, String uid, String orderCode);
ResponseDto<Boolean> changeExpressMailNo(String orderId, String mailId,String mailNo);
ResponseDto<Boolean> changeExpressMailNo(String orderId, String mailId, String mailNo);
}
package com.liquidnet.service.sweet.dto.param.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "SweetFormSubmissionActivityListParam", description = "动态表单报名活动列表查询")
public class SweetFormSubmissionActivityListParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)")
private String activityName;
}
package com.liquidnet.service.sweet.dto.param.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "SweetFormSubmissionExportParam", description = "动态表单报名导出")
public class SweetFormSubmissionExportParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)", required = true)
private String activityName;
@ApiModelProperty(value = "开始时间 yyyy-MM-dd HH:mm:ss")
private String beginTime;
@ApiModelProperty(value = "结束时间 yyyy-MM-dd HH:mm:ss")
private String endTime;
}
......@@ -47,7 +47,13 @@ public class CommonController
{
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
int underscoreIndex = fileName.indexOf("_");
String realFileName;
if (underscoreIndex > 0) {
realFileName = System.currentTimeMillis() + fileName.substring(underscoreIndex + 1);
} else {
realFileName = fileName;
}
String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
......
package com.liquidnet.client.admin.web.controller.zhengzai.sweet;
import com.github.pagehelper.PageHelper;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.PageDomain;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.core.page.TableSupport;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.client.admin.zhengzai.sweet.service.ISweetFormSubmissionAdminService;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Api(tags = "动态表单报名活动管理")
@Controller
@RequestMapping("/sweet/formSubmission")
public class SweetFormSubmissionAdminController extends BaseController {
private String prefix = "zhengzai/sweet/formSubmission";
@Autowired
private ISweetFormSubmissionAdminService sweetFormSubmissionAdminService;
@GetMapping("listView")
@ApiOperation(value = "报名活动列表页面")
public String listView() {
return prefix + "/list";
}
@Log(title = "报名活动列表", businessType = BusinessType.LIST)
@RequiresPermissions("sweet:formSubmission:list")
@PostMapping("list")
@ApiOperation(value = "报名活动列表数据")
@ResponseBody
public TableDataInfo list(SweetFormSubmissionActivityListParam param) {
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNull(pageNum)) {
pageNum = 1;
}
if (StringUtils.isNull(pageSize)) {
pageSize = 10;
}
// 聚合查询排序在 SQL 中固定,忽略前端 orderByColumn 避免 PageHelper 追加无效字段
PageHelper.startPage(pageNum, pageSize);
List<SweetFormSubmissionActivityVo> list = sweetFormSubmissionAdminService.activityList(param);
return getDataTable(list);
}
@Log(title = "报名数据导出", businessType = BusinessType.EXPORT)
@RequiresPermissions("sweet:formSubmission:export")
@PostMapping("export")
@ApiOperation(value = "报名数据导出")
@ResponseBody
public AjaxResult export(SweetFormSubmissionExportParam param) {
return sweetFormSubmissionAdminService.export(param);
}
}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('报名活动列表')"/>
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
活动:<select name="activityName" th:with="type=${@dict.getType('sweet_form_activity')}">
<option value="">全部</option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}"
th:value="${dict.dictValue}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i
class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i
class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-bordered">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer"/>
<script th:inline="javascript">
var exportFlag = [[${@permission.hasPermi('sweet:formSubmission:export')}]];
var prefix = ctx + "sweet/formSubmission";
function formatDateTime(value) {
return $.common.sprintf("<span>%s</span>", null != value ? value.replace("T", " ").substring(0, 19) : value);
}
function exportAll(activityName) {
doExport({activityName: activityName});
}
function exportByTime(activityName) {
var html = '<div style="padding: 20px;">' +
'<div class="form-group">' +
'<label>开始时间:</label>' +
'<input type="text" class="time-input form-control" id="exportBeginTime" placeholder="yyyy-MM-dd HH:mm:ss"/>' +
'</div>' +
'<div class="form-group">' +
'<label>结束时间:</label>' +
'<input type="text" class="time-input form-control" id="exportEndTime" placeholder="yyyy-MM-dd HH:mm:ss"/>' +
'</div>' +
'</div>';
layer.open({
type: 1,
title: '按时间导出',
area: ['420px', '260px'],
content: html,
btn: ['导出', '取消'],
success: function () {
layui.use('laydate', function () {
var laydate = layui.laydate;
laydate.render({elem: '#exportBeginTime', type: 'datetime', trigger: 'click'});
laydate.render({elem: '#exportEndTime', type: 'datetime', trigger: 'click'});
});
},
yes: function (index) {
var beginTime = $('#exportBeginTime').val();
var endTime = $('#exportEndTime').val();
if (!beginTime && !endTime) {
$.modal.msgWarning('请至少填写一个时间');
return;
}
layer.close(index);
doExport({activityName: activityName, beginTime: beginTime, endTime: endTime});
}
});
}
function doExport(data) {
$.modal.loading("正在导出数据,请稍后...");
$.post(prefix + "/export", data, function (result) {
$.modal.closeLoading();
if (result.code == web_status.SUCCESS) {
window.location.href = ctx + "common/download?fileName=" + encodeURI(result.msg) + "&delete=" + true;
} else {
$.modal.alertError(result.msg);
}
});
}
$(function () {
var options = {
url: prefix + "/list",
modalName: "报名活动",
sortable: false,
columns: [
{
field: 'activityName',
title: '活动标识(英文)'
},
{
field: 'activityLabel',
title: '活动名称(中文)',
formatter: function (value, row) {
return value || row.activityName;
}
},
{
field: 'submissionCount',
title: '报名数量'
},
{
field: 'firstCreatedAt',
title: '最早提交',
formatter: function (value) {
return formatDateTime(value);
}
},
{
field: 'lastCreatedAt',
title: '最新提交',
formatter: function (value) {
return formatDateTime(value);
}
},
{
title: '操作',
align: 'center',
formatter: function (value, row) {
var actions = [];
actions.push('<a class="btn btn-warning btn-xs ' + exportFlag + '" href="javascript:void(0)" onclick="exportAll(\'' + row.activityName + '\')"><i class="fa fa-download"></i> 全部导出</a>');
actions.push('<a class="btn btn-info btn-xs ' + exportFlag + '" href="javascript:void(0)" onclick="exportByTime(\'' + row.activityName + '\')"><i class="fa fa-clock-o"></i> 按时间导出</a>');
return actions.join(' ');
}
}
]
};
$.table.init(options);
});
</script>
</body>
</html>
package com.liquidnet.client.admin.zhengzai.sweet.service;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import java.util.List;
public interface ISweetFormSubmissionAdminService {
List<SweetFormSubmissionActivityVo> activityList(SweetFormSubmissionActivityListParam param);
AjaxResult export(SweetFormSubmissionExportParam param);
}
package com.liquidnet.client.admin.zhengzai.sweet.service.impl;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.domain.entity.SysDictData;
import com.liquidnet.client.admin.common.utils.DictUtils;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.client.admin.zhengzai.sweet.service.ISweetFormSubmissionAdminService;
import com.liquidnet.client.admin.zhengzai.sweet.utils.SweetFormSubmissionExcelUtils;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import com.liquidnet.service.sweet.mapper.SweetFormSubmissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SweetFormSubmissionAdminServiceImpl implements ISweetFormSubmissionAdminService {
public static final String DICT_TYPE_SWEET_FORM_ACTIVITY = "sweet_form_activity";
@Autowired
private SweetFormSubmissionMapper sweetFormSubmissionMapper;
@Override
public List<SweetFormSubmissionActivityVo> activityList(SweetFormSubmissionActivityListParam param) {
String activityName = StringUtils.isEmpty(param.getActivityName()) ? null : param.getActivityName();
List<SweetFormSubmissionActivityVo> list = sweetFormSubmissionMapper.selectActivityGroupList(activityName);
for (SweetFormSubmissionActivityVo vo : list) {
vo.setActivityLabel(getActivityLabel(vo.getActivityName()));
}
return list;
}
private String getActivityLabel(String activityName) {
if (StringUtils.isEmpty(activityName)) {
return "";
}
List<SysDictData> datas = DictUtils.getDictCache(DICT_TYPE_SWEET_FORM_ACTIVITY);
if (datas != null) {
for (SysDictData dict : datas) {
if (activityName.equals(dict.getDictValue())) {
return dict.getDictLabel();
}
}
}
return activityName;
}
@Override
public AjaxResult export(SweetFormSubmissionExportParam param) {
if (StringUtils.isEmpty(param.getActivityName())) {
return AjaxResult.error("请选择活动");
}
String beginTime = StringUtils.isEmpty(param.getBeginTime()) ? null : param.getBeginTime();
String endTime = StringUtils.isEmpty(param.getEndTime()) ? null : param.getEndTime();
List<SweetFormSubmission> submissions = sweetFormSubmissionMapper.selectForExport(
param.getActivityName(), beginTime, endTime);
String activityLabel = getActivityLabel(param.getActivityName());
String exportName = SweetFormSubmissionExcelUtils.buildExportName(activityLabel, beginTime, endTime);
String filename = SweetFormSubmissionExcelUtils.export(submissions, exportName);
return AjaxResult.success(filename);
}
}
package com.liquidnet.client.admin.zhengzai.sweet.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.liquidnet.client.admin.common.config.RuoYiConfig;
import com.liquidnet.client.admin.common.exception.BusinessException;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileOutputStream;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 动态表单报名 Excel 导出
*/
public class SweetFormSubmissionExcelUtils {
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final String COL_CREATED_AT = "提交时间";
private SweetFormSubmissionExcelUtils() {
}
public static String export(List<SweetFormSubmission> submissions, String exportName) {
if (submissions == null || submissions.isEmpty()) {
throw new BusinessException("查无报名信息");
}
List<Map<String, String>> rows = new ArrayList<>();
Set<String> dataKeys = new LinkedHashSet<>();
for (SweetFormSubmission submission : submissions) {
Map<String, String> row = new LinkedHashMap<>();
row.put(COL_CREATED_AT, submission.getCreatedAt() == null ? "" : submission.getCreatedAt().format(DTF));
parseDataJson(submission.getData(), row, dataKeys);
rows.add(row);
}
List<String> headers = new ArrayList<>();
headers.add(COL_CREATED_AT);
headers.addAll(dataKeys);
String sheetName = sanitizeSheetName(exportName);
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.size(); i++) {
headerRow.createCell(i).setCellValue(headers.get(i));
}
int rowNum = 1;
for (Map<String, String> rowData : rows) {
Row row = sheet.createRow(rowNum++);
for (int i = 0; i < headers.size(); i++) {
String value = rowData.get(headers.get(i));
row.createCell(i).setCellValue(value == null ? "" : value);
}
}
String filename = sanitizeFilename(exportName) + ".xlsx";
String absolutePath = RuoYiConfig.getDownloadPath() + filename;
File desc = new File(absolutePath);
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
try (FileOutputStream out = new FileOutputStream(desc)) {
workbook.write(out);
workbook.close();
} catch (Exception e) {
throw new BusinessException("导出Excel失败,请联系网站管理员!");
}
return filename;
}
public static String buildExportName(String activityLabel, String beginTime, String endTime) {
StringBuilder name = new StringBuilder(activityLabel).append("报名数据");
if (StringUtils.isNotEmpty(beginTime) || StringUtils.isNotEmpty(endTime)) {
name.append("_");
if (StringUtils.isNotEmpty(beginTime)) {
name.append(normalizeTimeForName(beginTime));
}
name.append("-");
if (StringUtils.isNotEmpty(endTime)) {
name.append(normalizeTimeForName(endTime));
}
}
return name.toString();
}
private static String normalizeTimeForName(String time) {
return time.trim().replace(":", "").replace(" ", "_");
}
private static String sanitizeSheetName(String name) {
if (StringUtils.isEmpty(name)) {
return "报名数据";
}
String sanitized = name.replaceAll("[\\\\/?*\\[\\]:]", "_");
return sanitized.length() > 31 ? sanitized.substring(0, 31) : sanitized;
}
private static String sanitizeFilename(String name) {
if (StringUtils.isEmpty(name)) {
return "报名数据";
}
return name.replaceAll("[\\\\/:*?\"<>|]", "_");
}
private static void parseDataJson(String dataJson, Map<String, String> row, Set<String> dataKeys) {
if (dataJson == null || dataJson.trim().isEmpty()) {
return;
}
try {
JSONObject jsonObject = JSON.parseObject(dataJson, Feature.OrderedField);
if (jsonObject == null) {
return;
}
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
String key = entry.getKey();
if (!dataKeys.contains(key)) {
dataKeys.add(key);
}
row.put(key, formatValue(entry.getValue()));
}
} catch (Exception ignored) {
if (!dataKeys.contains("data")) {
dataKeys.add("data");
}
row.put("data", dataJson);
}
}
private static String formatValue(Object value) {
if (value == null) {
return "";
}
if (value instanceof Map || value instanceof List) {
return JSON.toJSONString(value);
}
return String.valueOf(value);
}
}
package com.liquidnet.service.sweet.dto.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@ApiModel(value = "SweetFormSubmissionActivityVo", description = "动态表单报名活动汇总")
public class SweetFormSubmissionActivityVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)")
private String activityName;
@ApiModelProperty(value = "活动名称(中文)")
private String activityLabel;
@ApiModelProperty(value = "报名数量")
private Long submissionCount;
@ApiModelProperty(value = "最早提交时间")
private LocalDateTime firstCreatedAt;
@ApiModelProperty(value = "最新提交时间")
private LocalDateTime lastCreatedAt;
}
package com.liquidnet.service.sweet.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 动态表单报名提交表 Mapper
*/
public interface SweetFormSubmissionMapper extends BaseMapper<SweetFormSubmission> {
List<SweetFormSubmissionActivityVo> selectActivityGroupList(@Param("activityName") String activityName);
List<SweetFormSubmission> selectForExport(@Param("activityName") String activityName,
@Param("beginTime") String beginTime,
@Param("endTime") String endTime);
}
......@@ -2,4 +2,32 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liquidnet.service.sweet.mapper.SweetFormSubmissionMapper">
<select id="selectActivityGroupList" resultType="com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo">
SELECT activity_name AS activityName,
COUNT(1) AS submissionCount,
MIN(created_at) AS firstCreatedAt,
MAX(created_at) AS lastCreatedAt
FROM sweet_form_submission
<where>
<if test="activityName != null and activityName != ''">
AND activity_name = #{activityName}
</if>
</where>
GROUP BY activity_name
ORDER BY MAX(created_at) DESC
</select>
<select id="selectForExport" resultType="com.liquidnet.service.sweet.entity.SweetFormSubmission">
SELECT mid, submission_id AS submissionId, activity_name AS activityName, data, created_at AS createdAt, updated_at AS updatedAt
FROM sweet_form_submission
WHERE activity_name = #{activityName}
<if test="beginTime != null and beginTime != ''">
AND created_at &gt;= #{beginTime}
</if>
<if test="endTime != null and endTime != ''">
AND created_at &lt;= #{endTime}
</if>
ORDER BY created_at ASC
</select>
</mapper>
package com.liquidnet.service.goblin.controller.manage;
import com.github.pagehelper.PageInfo;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.service.base.ResponseDto;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import com.liquidnet.service.goblin.param.GoblinStoreOrderRefundParam;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreBackOrderService;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
......@@ -41,17 +37,17 @@ public class GoblinStoreBackOrderController {
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "申请终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderCode", value = "订单编号"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "spuName", value = "商品名称"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[1-待处理|2-已退款|9-已拒绝|11-已取消|0-退款中|7-退货中]"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "status", value = "审核状态,多个用英文逗号分隔。0-商铺发起退款|1-退款申请|2-退款成功|3-退款拒绝|4-退货申请|5-退货拒绝|6-待填物流|7-待收货|8-退货完成|9-退货失败|10-退款失败|11-取消退款"),
})
@GetMapping(value = "list")
public ResponseDto<PageInfo<GoblinStoreBackOrderListVo>> orderBackList(@RequestParam(value = "page", required = true) @Valid Integer page,
public ResponseDto<GoblinStoreBackOrderListPageVo> orderBackList(@RequestParam(value = "page", required = true) @Valid Integer page,
@RequestParam(value = "orderBackCode", required = false) String orderBackCode,
@RequestParam(value = "type", required = false) Integer type,
@RequestParam(value = "cst", required = false) String cst,
@RequestParam(value = "cet", required = false) String cet,
@RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "spuName", required = false) String spuName,
@RequestParam(value = "status", required = false) Integer status) {
@RequestParam(value = "status", required = false) String status) {
return goblinStoreBackOrderService.orderBackList(page, orderBackCode, type, cst, cet, orderCode, spuName, status);
}
......@@ -83,6 +79,12 @@ public class GoblinStoreBackOrderController {
return goblinStoreBackOrderService.refusedRefund(backOrderId);
}
@ApiOperation(value = "发起退款")
@PostMapping(value = "refund")
public ResponseDto<Boolean> refundOrder(@RequestBody @Valid GoblinStoreOrderRefundParam param) {
return goblinStoreBackOrderService.refundOrder(param);
}
@ApiOperation(value = "修改金额")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "backOrderId", value = "订单id"),
......
package com.liquidnet.service.goblin.controller.manage;
import com.github.pagehelper.PageInfo;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinMailVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import com.liquidnet.service.goblin.service.IGoblinOrderService;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService;
import io.swagger.annotations.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.List;
@ApiSupport(order = 149101)
@Api(tags = "店铺-订单")
......@@ -40,10 +43,10 @@ public class GoblinStoreOrderController {
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "下单终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "expressContacts", value = "收货人姓名"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "phone", value = "手机号"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-代付款|2-待发货|3-已发货|4-已完成]"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-待支付|2-待发货|3-已发货|4-已完成|6-已退款]"),
})
@GetMapping(value = "list")
public ResponseDto<PageInfo<GoblinStoreOrderListVo>> orderList(@RequestParam(value = "page", required = true) @Valid Integer page,
public ResponseDto<GoblinStoreOrderListPageVo> orderList(@RequestParam(value = "page", required = true) @Valid Integer page,
@RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "type", required = false) Integer type,
@RequestParam(value = "cst", required = false) String cst,
......@@ -55,6 +58,26 @@ public class GoblinStoreOrderController {
return goblinStoreOrderService.orderList(page, orderCode, type, cst, cet, expressContacts, phone, status);
}
@ApiOperation(value = "订单列表导出")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderCode", value = "订单编号"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cst", value = "下单起始时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "下单终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "expressContacts", value = "收货人姓名"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "phone", value = "手机号"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-代付款|2-待发货|3-已发货|4-已完成]"),
})
@GetMapping(value = "list/export")
public void exportOrderList(HttpServletResponse response,
@RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "cst", required = false) String cst,
@RequestParam(value = "cet", required = false) String cet,
@RequestParam(value = "expressContacts", required = false) String expressContacts,
@RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "status", required = false) Integer status) {
goblinStoreOrderService.exportOrderList(response, orderCode, cst, cet, expressContacts, phone, status);
}
@ApiOperation(value = "订单详情")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"),
......@@ -112,7 +135,7 @@ public class GoblinStoreOrderController {
return goblinStoreOrderService.changeAddress(orderId, expressContacts, expressPhone, expressAddressDetail);
}
@ApiOperation(value = "退款(sku/整单)")
@ApiOperation(value = "退款(sku/整单)-废弃")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderSkuId", value = "orderSkuId[整单退不传]"),
......@@ -135,7 +158,7 @@ public class GoblinStoreOrderController {
public ResponseDto<Boolean> express(@RequestParam(value = "orderId", required = true) @Valid String orderId,
@RequestParam(value = "orderSkuIds", required = false) @Valid String orderSkuIds,
@RequestParam(value = "mailNo", required = true) @Valid String mailNo) {
return goblinStoreOrderService.express(orderId, orderSkuIds, mailNo, CurrentUtil.getCurrentUid(),"");
return goblinStoreOrderService.express(orderId, orderSkuIds, mailNo, CurrentUtil.getCurrentUid(), "");
}
@ApiOperation(value = "修改快递单号")
......
......@@ -6,6 +6,7 @@ import com.liquidnet.common.third.sqb.param.response.data.CouponQueryData;
import com.liquidnet.common.third.sqb.param.response.data.CouponRefundData;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.*;
......@@ -116,7 +117,7 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
@Override
public ResponseDto<Boolean> refund(String userId, String orderId, String reason) {
log.info("[收钱吧退款] 开始 userId={}, orderId={}", userId, orderId);
log.info("[收钱吧-退款] 开始 userId={}, orderId={}", userId, orderId);
// 与详情接口一致:先主单 store + 商品,再收钱吧扩展;避免扩展单缺失却提示「订单不存在」
GoblinStoreOrderVo storeOrderVo = goblinRedisUtils.getGoblinOrder(orderId);
......@@ -182,6 +183,7 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
reason,
buildRequestId(IDGenerator.nextSnowId())
);
log.info("[收钱吧-退款] 收钱吧退款响应: {}, orderId: {}.", JsonUtils.toJson(refundData), orderId);
if (refundData == null) {
// 退款失败,回滚状态
orderVo.setUpdatedAt(LocalDateTime.now().format(DTF));
......@@ -198,8 +200,8 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
goblinSqbRedisUtils.setSqbOrder(orderId, orderVo);
goblinRedisUtils.incrSkuStock(null, orderVo.getSkuId(), orderVo.getQuantity());
syncOrderStatus(orderId, 3);
log.info("[收钱吧退款] 退款成功,orderId={}", orderId);
// syncOrderStatus(orderId, 3);
log.info("[收钱吧-退款] 调用收钱吧退款成功,orderId={}", orderId);
return ResponseDto.success(Boolean.TRUE);
} catch (Exception e) {
......
package com.liquidnet.service.goblin.service.impl.helper;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.HttpUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.goblin.constant.GoblinStatusConst;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderSkuVo;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderVo;
import com.liquidnet.service.goblin.dto.vo.GoblinOrderSkuVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderVo;
import com.liquidnet.service.goblin.util.GoblinRedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import java.math.BigDecimal;
import java.util.HashMap;
@Component
@Slf4j
public class GoblinRefundHelper {
@Autowired
private GoblinRedisUtils redisUtils;
@Value("${liquidnet.service.order.url-pay.goblinRefundUrl}")
private String synUrl;
@Value("${liquidnet.service.dragon.urls.refundApply}")
private String refundApply;
@Value("${liquidnet.service.order.url-pay.goblinRefundUrl}")
private String goblinRefundUrl;
@Value("${liquidnet.service.dragon.urls.refundResult}")
private String refundApplyCallBack;
public static BigDecimal remainRefundPrice(GoblinOrderSkuVo orderSkuVo, BigDecimal applyingRefundPrice) {
BigDecimal priceActual = nullToZero(orderSkuVo.getSkuPriceActual());
BigDecimal priceRefund = nullToZero(orderSkuVo.getPriceRefund());
BigDecimal applying = nullToZero(applyingRefundPrice);
BigDecimal remain = priceActual.subtract(priceRefund).subtract(applying);
return remain.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : remain;
}
public static BigDecimal beforeShipRefundPrice(BigDecimal skuRefundPrice, BigDecimal priceExpress) {
return nullToZero(skuRefundPrice).add(nullToZero(priceExpress));
}
public static GoblinBackOrderSkuVo buildBackOrderSkuVo(GoblinOrderSkuVo orderSkuVo, BigDecimal refundPrice, String nowStr) {
GoblinBackOrderSkuVo backOrderSkuVo = GoblinBackOrderSkuVo.getNew();
backOrderSkuVo.setOrderSkuId(orderSkuVo.getOrderSkuId());
backOrderSkuVo.setSpuId(orderSkuVo.getSpuId());
backOrderSkuVo.setSpuName(orderSkuVo.getSpuName());
backOrderSkuVo.setSkuId(orderSkuVo.getSkuId());
backOrderSkuVo.setSkuName(orderSkuVo.getSkuName());
backOrderSkuVo.setRefundPrice(refundPrice);
backOrderSkuVo.setSkuSpecs(orderSkuVo.getSkuSpecs());
backOrderSkuVo.setCreatedAt(nowStr);
backOrderSkuVo.setSkuPic(orderSkuVo.getSkuImage());
backOrderSkuVo.setSkuType(orderSkuVo.getSkuType());
return backOrderSkuVo;
}
public static boolean isBackOrderFinished(Integer status) {
return status != null && (status == 2 || status == 3 || status == 5 || status == 8 || status == 9 || status == 10 || status == 11);
}
public static boolean isUnshippedSku(GoblinOrderSkuVo orderSkuVo) {
return orderSkuVo != null && orderSkuVo.getStatus() == 2;
}
public static boolean isShippedSku(GoblinOrderSkuVo orderSkuVo) {
if (orderSkuVo == null) {
return false;
}
return orderSkuVo.getStatus() == 3 || orderSkuVo.getStatus() == 4;
}
/**
* 推断退款单发起方:优先读已存储值;历史数据无 operationType 时按 describes/reason 兜底。
*/
public static Integer resolveBackOrderOperationType(GoblinBackOrderVo backOrderVo) {
if (backOrderVo == null) {
return GoblinStatusConst.Type.OPERATION_TYPE_1.getValue();
}
if (backOrderVo.getOperationType() != null) {
return backOrderVo.getOperationType();
}
if ("店铺退款".equals(backOrderVo.getDescribes())
|| GoblinStatusConst.Type.BACK_REASON_TYPE_8.getDesc().equals(backOrderVo.getReason())) {
return GoblinStatusConst.Type.OPERATION_TYPE_2.getValue();
}
return GoblinStatusConst.Type.OPERATION_TYPE_1.getValue();
}
public String initRefund(GoblinStoreOrderVo orderVo, BigDecimal price, String refundCode) {
MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
params.add("code", orderVo.getPayCode());
params.add("notifyUrl", synUrl);
params.add("orderCode", orderVo.getMasterOrderCode());
params.add("orderRefundCode", refundCode);
params.add("paymentId", orderVo.getPaymentId());
params.add("paymentType", orderVo.getPaymentType());
params.add("price", String.valueOf(price));
params.add("priceTotal", String.valueOf(getMasterOrderActualPrice(orderVo)));
params.add("reason", "按需退款");
MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString();
headers.add("Accept", "application/json;charset=UTF-8");
String returnString = HttpUtil.post(refundApply, params, headers);
log.debug("REFUND DATA = " + returnString);
return returnString;
}
public String alipayCallBack(GoblinStoreOrderVo orderVo, String refundCode) {
MultiValueMap<String, String> params = CollectionUtil.linkedMultiValueMapStringString();
params.add("callBackUrl", goblinRefundUrl);
params.add("orderCode", orderVo.getMasterOrderCode());
params.add("orderRefundCode", refundCode);
params.add("paymentId", orderVo.getPaymentId());
MultiValueMap<String, String> headers = CollectionUtil.linkedMultiValueMapStringString();
headers.add("Accept", "application/json;charset=UTF-8");
log.debug("REFUND CALLBACK params = " + params);
String returnString = HttpUtil.post(refundApplyCallBack, params, headers);
log.debug("REFUND CALLBACK DATA = " + returnString);
return returnString;
}
public boolean isRefundSuccess(String returnString) {
HashMap hashMapResult = JsonUtils.fromJson(returnString, HashMap.class);
Boolean success = (Boolean) hashMapResult.get("success");
return Boolean.TRUE.equals(success);
}
public String getRefundMessage(String returnString) {
HashMap hashMapResult = JsonUtils.fromJson(returnString, HashMap.class);
String message = (String) hashMapResult.get("message");
return message == null ? "" : message;
}
private BigDecimal getMasterOrderActualPrice(GoblinStoreOrderVo orderVo) {
BigDecimal totalPrice = BigDecimal.ZERO;
String[] orderIds = redisUtils.getMasterCode(orderVo.getMasterOrderCode());
if (orderIds == null || orderIds.length == 0) {
return nullToZero(orderVo.getPriceActual());
}
for (String orderId : orderIds) {
GoblinStoreOrderVo vo = redisUtils.getGoblinOrder(orderId);
if (vo != null) {
totalPrice = totalPrice.add(nullToZero(vo.getPriceActual()));
}
}
return totalPrice.compareTo(BigDecimal.ZERO) > 0 ? totalPrice : nullToZero(orderVo.getPriceActual());
}
private static BigDecimal nullToZero(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
}
......@@ -129,6 +129,7 @@ goblin_order.store.log=INSERT INTO goblin_order_operation_log (`order_log_id`,`o
goblin_order.store.refundLog=INSERT INTO goblin_back_order_log (`back_order_log_id`,`back_order_id`,`operation_type`,`message`,`operation_name`,`status`,`created_at`) VALUES(?,?,?,?,?,?,?)
goblin_order.store.orderExpressPrice=UPDATE goblin_store_order SET price_total =? , price_modify = ? ,price_voucher = ? , price_actual = ? ,price_express = ? , updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.backOrder=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`status`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.store.backOrderWithExpress=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`pics`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.store.changeExpress=UPDATE goblin_back_order SET real_back_price = ? ,back_price_express = ? updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.changeSku=UPDATE goblin_back_order SET real_back_price = ? , updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.backOrderStatus=UPDATE goblin_back_order SET status = ? , refuse_at=?,refuse_size=?,updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
......@@ -137,6 +138,7 @@ goblin_order.store.orderStatus.time=UPDATE goblin_store_order SET status = ? ,zh
goblin_order.store.applyRefund=UPDATE goblin_back_order SET status = ? ,reason=?,audit_at=?, updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
#---- \u7528\u6237\u8BA2\u5355\u64CD\u4F5C
goblin_order.user.applyRefund=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`logis_company_name`,`mail_no`,`pics`,`created_at`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.user.applyRefundAuto=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`logis_company_name`,`mail_no`,`pics`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.user.againRefund=UPDATE goblin_back_order SET status = ? , describes = ? , pics = ? , reason = ? , type = ?, updated_at=? where back_order_id=?
#---- \u8D2D\u7269\u8F66\u64CD\u4F5C
goblin_shop.cart.delete=UPDATE goblin_shopping_cart set del_tag=? where user_id=? and sku_id=?
......
package com.liquidnet.service.goblin.test;
import com.liquidnet.service.goblin.dto.vo.GoblinOrderSkuVo;
import com.liquidnet.service.goblin.service.impl.helper.GoblinRefundHelper;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class GoblinRefundHelperTest {
@Test
public void remainRefundPriceSubtractsRefundedAndApplyingAmount() {
GoblinOrderSkuVo skuVo = new GoblinOrderSkuVo();
skuVo.setSkuPriceActual(new BigDecimal("100.00"));
skuVo.setPriceRefund(new BigDecimal("30.00"));
BigDecimal remain = GoblinRefundHelper.remainRefundPrice(skuVo, new BigDecimal("20.00"));
Assert.assertEquals(0, new BigDecimal("50.00").compareTo(remain));
}
@Test
public void remainRefundPriceNeverReturnsNegativeAmount() {
GoblinOrderSkuVo skuVo = new GoblinOrderSkuVo();
skuVo.setSkuPriceActual(new BigDecimal("10.00"));
skuVo.setPriceRefund(new BigDecimal("8.00"));
BigDecimal remain = GoblinRefundHelper.remainRefundPrice(skuVo, new BigDecimal("5.00"));
Assert.assertEquals(0, BigDecimal.ZERO.compareTo(remain));
}
@Test
public void beforeShipRefundIncludesExpressFee() {
BigDecimal refundPrice = GoblinRefundHelper.beforeShipRefundPrice(new BigDecimal("80.00"), new BigDecimal("12.00"));
Assert.assertEquals(0, new BigDecimal("92.00").compareTo(refundPrice));
}
}
......@@ -1253,22 +1253,6 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
List<GoblinBackOrderSkuVo> backOrderSkuVos = backOrderVo.getBackOrderSkuVos();
GoblinStoreOrderVo orderVo = redisUtils.getGoblinOrder(orderId);
orderVo.setPriceRefund(orderVo.getPriceRefund() == null ? BigDecimal.ZERO : orderVo.getPriceRefund().add(refundCallbackParam.getRefundPrice()));
if (orderVo.getPriceRefund().compareTo(orderVo.getPriceActual()) >= 0) {
//整单退款 退券
orderVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_6.getValue());
if (!(orderVo.getUcouponId() == null || orderVo.getUcouponId().equals(""))) {
orderUtils.backCoupon(orderVo.getUcouponId(), orderVo.getUserId());
}
if (!(orderVo.getStoreCouponId() == null || orderVo.getStoreCouponId().equals(""))) {
List<BackCouponParam> params = ObjectUtil.getBackCouponParam();
BackCouponParam backCouponParam = BackCouponParam.getNew();
backCouponParam.setuCouponIds(orderVo.getStoreCouponId());
backCouponParam.setUid(orderVo.getUserId());
params.add(backCouponParam);
orderUtils.backStoreCoupon(params);
}
}
backOrderVo.setStatus(GoblinStatusConst.Status.ORDER_BACK_STATUS_2.getValue());
backOrderVo.setRefundAt(nowStr);
for (GoblinBackOrderSkuVo backOrderSkuVo : backOrderSkuVos) {
......@@ -1322,6 +1306,20 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
logVo.setCreatedAt(now);
mongoUtils.insertGoblinOrderLogVo(logVo);
}
if (isOrderFullyRefunded(orderVo)) {
orderVo.setStatus(GoblinStatusConst.Status.ORDER_STATUS_6.getValue());
if (!(orderVo.getUcouponId() == null || orderVo.getUcouponId().equals(""))) {
orderUtils.backCoupon(orderVo.getUcouponId(), orderVo.getUserId());
}
if (!(orderVo.getStoreCouponId() == null || orderVo.getStoreCouponId().equals(""))) {
List<BackCouponParam> params = ObjectUtil.getBackCouponParam();
BackCouponParam backCouponParam = BackCouponParam.getNew();
backCouponParam.setuCouponIds(orderVo.getStoreCouponId());
backCouponParam.setUid(orderVo.getUserId());
params.add(backCouponParam);
orderUtils.backStoreCoupon(params);
}
}
//redis
redisUtils.setGoblinOrder(orderId, orderVo);
redisUtils.setBackOrderVo(backOrderVo.getBackOrderId(), backOrderVo);
......@@ -1479,4 +1477,26 @@ public class GoblinOrderServiceImpl implements IGoblinOrderService {
return "";
}
/**
* 主单是否已全部退完:以所有子单实退金额为准,不再要求累计退款金额达到主单实付(含运费)。
* 已发货后部分退不含运费时,商品全部退完也应进入退款通过;待发货整单退含运费时,子单同样会全部退完。
*/
private boolean isOrderFullyRefunded(GoblinStoreOrderVo orderVo) {
if (orderVo == null || orderVo.getOrderSkuVoIds() == null || orderVo.getOrderSkuVoIds().isEmpty()) {
return false;
}
for (String orderSkuId : orderVo.getOrderSkuVoIds()) {
GoblinOrderSkuVo orderSkuVo = redisUtils.getGoblinOrderSkuVo(orderSkuId);
if (orderSkuVo == null) {
return false;
}
BigDecimal priceRefund = orderSkuVo.getPriceRefund() == null ? BigDecimal.ZERO : orderSkuVo.getPriceRefund();
BigDecimal skuPriceActual = orderSkuVo.getSkuPriceActual() == null ? BigDecimal.ZERO : orderSkuVo.getSkuPriceActual();
if (priceRefund.compareTo(skuPriceActual) < 0) {
return false;
}
}
return true;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment