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

Commit 6d70c683 authored by 姜秀龙's avatar 姜秀龙

退款归因

parent 8d25b760
-- 演出票退款原因配置表 + 退款单归因字段
CREATE TABLE IF NOT EXISTS `kylin_order_refund_reasons` (
`mid` int(11) NOT NULL AUTO_INCREMENT,
`reason_id` varchar(32) NOT NULL DEFAULT '' COMMENT '业务ID',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '退款理由名称',
`need_detail` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否需要二级补充输入 0否 1是',
`placeholder` varchar(200) NOT NULL DEFAULT '' COMMENT '二级输入placeholder',
`is_applied` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已应用 0未应用 1应用中',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序,越大越靠前',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_reason_id` (`reason_id`),
KEY `idx_is_applied_sort` (`is_applied`, `sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='演出票退款原因配置';
ALTER TABLE `kylin_order_refunds`
ADD COLUMN `reason_id` varchar(32) DEFAULT NULL COMMENT '退款原因配置ID' AFTER `reason`,
ADD COLUMN `reason_detail` varchar(200) DEFAULT NULL COMMENT '退款原因二级补充说明' AFTER `reason_id`;
-- 演出票退款原因管理 - 菜单与权限
-- 挂载在「正在映画」顶级菜单下
SET @zhengzaiRootId = (
SELECT menu_id
FROM sys_menu
WHERE menu_name = '正在映画'
AND parent_id = 0
AND menu_type = 'M'
LIMIT 1
);
SET @refundParentId = (
SELECT menu_id
FROM sys_menu
WHERE menu_name LIKE '%退款%'
AND parent_id = @zhengzaiRootId
AND menu_type = 'M'
LIMIT 1
);
-- 若无退款父菜单则挂到正在映画根下
SET @parentId = IFNULL(@refundParentId, @zhengzaiRootId);
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, 20, '/kylin/refundReasons', 'menuItem', 'C', '0', 'kylin:refundReasons:list', 'fa fa-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', 'kylin:refundReasons:list', '#', 'admin', NOW(), '', NULL, ''),
('退款原因新增', @menuId, 2, '#', '', 'F', '0', 'kylin:refundReasons:create', '#', 'admin', NOW(), '', NULL, ''),
('退款原因修改', @menuId, 3, '#', '', 'F', '0', 'kylin:refundReasons:update', '#', 'admin', NOW(), '', NULL, ''),
('退款原因删除', @menuId, 4, '#', '', 'F', '0', 'kylin:refundReasons:delete', '#', 'admin', NOW(), '', NULL, ''),
('退款原因应用', @menuId, 5, '#', '', 'F', '0', 'kylin:refundReasons:apply', '#', 'admin', NOW(), '', NULL, '');
......@@ -96,6 +96,11 @@ public class KylinRedisConst {
public static final String KYLIN_EXPRESS_MODULES = "kylin:express:modules";
/**
* 已应用的演出票退款原因列表
*/
public static final String KYLIN_ORDER_REFUND_REASONS_APPLIED = "kylin:order:refund:reasons:applied";
public static final String REDIS_KEY_KYLIN_PERFORM_SUBSCRIBE = "kylin:performSubscribe:uid:";
public static final String REDIS_KEY_KYLIN_PERFORMANCE_SUBSCRIBE = "kylin:performance:subscribe:"; //演出预约
......
package com.liquidnet.service.kylin.dto.param;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.time.LocalDateTime;
import java.util.List;
/**
* 退款原因配置参数
*/
@Data
@ApiModel("退款原因配置参数")
public class OrderRefundReasonParam {
@ApiModelProperty(value = "原因业务ID")
private String reasonId;
@ApiModelProperty(value = "退款理由名称")
private String name;
@ApiModelProperty(value = "是否需要二级补充输入 0否 1是")
private Integer needDetail;
@ApiModelProperty(value = "二级输入placeholder")
private String placeholder;
@ApiModelProperty(value = "是否已应用 0未应用 1应用中")
private Integer isApplied;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "批量操作ID列表")
private List<String> ids;
public KylinOrderRefundReasons getFields(String reasonId, LocalDateTime createdAt) {
KylinOrderRefundReasons entity = new KylinOrderRefundReasons();
BeanUtils.copyProperties(this, entity);
if (reasonId != null) {
entity.setReasonId(reasonId);
}
if (createdAt != null) {
entity.setCreatedAt(createdAt);
}
if (entity.getNeedDetail() == null) {
entity.setNeedDetail(0);
}
if (entity.getPlaceholder() == null) {
entity.setPlaceholder("");
}
if (entity.getIsApplied() == null) {
entity.setIsApplied(0);
}
if (entity.getSort() == null) {
entity.setSort(0);
}
if (entity.getNeedDetail() != null && entity.getNeedDetail() == 0) {
entity.setPlaceholder("");
}
entity.setUpdatedAt(LocalDateTime.now());
return entity;
}
}
package com.liquidnet.service.kylin.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 退款原因配置搜索参数
*/
@Data
@ApiModel("退款原因配置搜索参数")
public class OrderRefundReasonSearchParam {
@ApiModelProperty(value = "退款理由名称")
private String name;
@ApiModelProperty(value = "是否已应用 0未应用 1应用中")
private Integer isApplied;
@ApiModelProperty(value = "页码")
private Integer pageNum = 1;
@ApiModelProperty(value = "每页数量")
private Integer pageSize = 10;
}
......@@ -23,6 +23,16 @@ public class RefundApplyParam implements Serializable {
private String reason;
/**
* 退款原因配置ID
*/
private String reasonId;
/**
* 退款原因二级补充说明
*/
private String reasonDetail;
private BigDecimal RefundPriceExpress;
private List<String> ticketEntityIds;
......
......@@ -23,6 +23,16 @@ public class RefundBatchApplyParam implements Serializable {
private String reason;
/**
* 退款原因配置ID
*/
private String reasonId;
/**
* 退款原因二级补充说明
*/
private String reasonDetail;
private Integer status;
private Integer refundStatus;
......
package com.liquidnet.service.kylin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 退款原因配置 VO
*/
@Data
@ApiModel("退款原因配置")
public class OrderRefundReasonVo implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "原因业务ID")
private String reasonId;
@ApiModelProperty(value = "退款理由名称")
private String name;
@ApiModelProperty(value = "是否需要二级补充输入 0否 1是")
private Integer needDetail;
@ApiModelProperty(value = "二级输入placeholder")
private String placeholder;
@ApiModelProperty(value = "是否已应用 0未应用 1应用中")
private Integer isApplied;
@ApiModelProperty(value = "应用状态名称")
private String isAppliedName;
@ApiModelProperty(value = "排序")
private Integer sort;
@ApiModelProperty(value = "创建时间")
private String createdAt;
@ApiModelProperty(value = "修改时间")
private String updatedAt;
private static final OrderRefundReasonVo OBJ = new OrderRefundReasonVo();
public static OrderRefundReasonVo getNew() {
try {
return (OrderRefundReasonVo) OBJ.clone();
} catch (CloneNotSupportedException e) {
return new OrderRefundReasonVo();
}
}
}
......@@ -91,6 +91,12 @@ public class KylinOrderRefundsAdminVo implements Serializable,Cloneable {
@ApiModelProperty(value = "退款原因")
private String reason;
@ApiModelProperty(value = "退款原因配置ID")
private String reasonId;
@ApiModelProperty(value = "退款原因二级补充说明")
private String reasonDetail;
@ApiModelProperty(value = "审核人id")
private String auditorId;
......
......@@ -89,6 +89,12 @@ public class KylinOrderRefundsVo implements Serializable,Cloneable {
@ApiModelProperty(value = "退款原因")
private String reason;
@ApiModelProperty(value = "退款原因配置ID")
private String reasonId;
@ApiModelProperty(value = "退款原因二级补充说明")
private String reasonDetail;
@ApiModelProperty(value = "审核人id")
private String auditorId;
......
package com.liquidnet.service.kylin.service;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import java.util.List;
/**
* 退款原因(App/H5)
*/
public interface IKylinOrderRefundReasonService {
/**
* 已应用退款理由列表(Redis 缓存)
*/
List<OrderRefundReasonVo> listApplied();
/**
* 按 reasonId 从已应用列表取配置;不存在返回 null
*/
OrderRefundReasonVo getAppliedByReasonId(String reasonId);
}
package com.liquidnet.service.kylin.service.admin;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonParam;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonSearchParam;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import java.util.List;
/**
* 退款原因配置管理
*/
public interface IOrderRefundReasonAdminService {
Boolean create(OrderRefundReasonParam param);
Boolean update(OrderRefundReasonParam param);
OrderRefundReasonVo detail(String reasonId);
Boolean delete(List<String> reasonIds);
PageInfo<KylinOrderRefundReasons> reasonList(OrderRefundReasonSearchParam param);
/**
* 应用 / 取消应用
*
* @param reasonId 原因ID
* @param isApplied 1应用 0取消应用
*/
Boolean apply(String reasonId, Integer isApplied);
/**
* 已应用列表(读缓存)
*/
List<OrderRefundReasonVo> listApplied();
}
package com.liquidnet.service.kylin.utils;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* 退款原因展示文案与校验工具
*/
public final class OrderRefundReasonUtils {
private OrderRefundReasonUtils() {
}
/**
* 校验已应用退款原因,并组装落库字段
*
* @param reasonId 原因配置 ID
* @param reasonDetail 二级补充说明
* @param appliedList 已应用原因列表(可从缓存取)
*/
public static ResolvedRefundReason resolve(String reasonId, String reasonDetail, List<OrderRefundReasonVo> appliedList) {
if (StringUtils.isBlank(reasonId)) {
return ResolvedRefundReason.fail("请选择退款原因");
}
OrderRefundReasonVo matched = findByReasonId(reasonId, appliedList);
if (matched == null) {
return ResolvedRefundReason.fail("退款原因无效或未应用");
}
return resolve(matched, reasonDetail);
}
/**
* 已拿到配置项时的校验与组装
*/
public static ResolvedRefundReason resolve(OrderRefundReasonVo matched, String reasonDetail) {
if (matched == null) {
return ResolvedRefundReason.fail("退款原因无效或未应用");
}
String detail = StringUtils.trimToEmpty(reasonDetail);
boolean needDetail = matched.getNeedDetail() != null && matched.getNeedDetail() == 1;
if (needDetail && StringUtils.isBlank(detail)) {
return ResolvedRefundReason.fail("请填写补充说明");
}
if (!needDetail) {
detail = null;
} else {
detail = truncate(detail, 200);
}
String snapshot = buildReasonSnapshot(matched.getName(), detail);
return ResolvedRefundReason.ok(matched.getReasonId(), detail, snapshot);
}
public static OrderRefundReasonVo findByReasonId(String reasonId, List<OrderRefundReasonVo> appliedList) {
if (StringUtils.isBlank(reasonId) || appliedList == null) {
return null;
}
for (OrderRefundReasonVo item : appliedList) {
if (reasonId.equals(item.getReasonId())) {
return item;
}
}
return null;
}
/**
* 组装 reason 快照:无二级写 name;有二级写 name:detail,截断至 200
*/
public static String buildReasonSnapshot(String name, String reasonDetail) {
String base = StringUtils.defaultString(name, "");
if (StringUtils.isBlank(reasonDetail)) {
return truncate(base, 200);
}
return truncate(base + ":" + reasonDetail.trim(), 200);
}
public static String truncate(String value, int maxLen) {
if (value == null) {
return "";
}
if (value.length() <= maxLen) {
return value;
}
return value.substring(0, maxLen);
}
@Getter
public static final class ResolvedRefundReason {
private final boolean success;
private final String errorMsg;
private final String reasonId;
private final String reasonDetail;
private final String reasonSnapshot;
private ResolvedRefundReason(boolean success, String errorMsg, String reasonId, String reasonDetail, String reasonSnapshot) {
this.success = success;
this.errorMsg = errorMsg;
this.reasonId = reasonId;
this.reasonDetail = reasonDetail;
this.reasonSnapshot = reasonSnapshot;
}
public static ResolvedRefundReason ok(String reasonId, String reasonDetail, String reasonSnapshot) {
return new ResolvedRefundReason(true, null, reasonId, reasonDetail, reasonSnapshot);
}
public static ResolvedRefundReason fail(String errorMsg) {
return new ResolvedRefundReason(false, errorMsg, null, null, null);
}
}
}
package com.liquidnet.client.admin.web.controller.zhengzai.kylin;
import com.github.pagehelper.PageInfo;
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.TableDataInfo;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.zhengzai.kylin.service.impl.KylinOrderRefundReasonAdminServiceImpl;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonParam;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonSearchParam;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 退款原因管理
*/
@Controller
@RequestMapping("/kylin/refundReasons")
public class KylinOrderRefundReasonsAdminController extends BaseController {
private String prefix = "zhengzai/kylin/refundReasons";
@Autowired
private KylinOrderRefundReasonAdminServiceImpl refundReasonAdminService;
@RequiresPermissions("kylin:refundReasons:list")
@GetMapping()
public String list() {
return prefix + "/refundReasons";
}
@RequiresPermissions("kylin:refundReasons:list")
@PostMapping("list")
@ResponseBody
public TableDataInfo listData(OrderRefundReasonSearchParam param) {
PageInfo<KylinOrderRefundReasons> result = refundReasonAdminService.reasonList(param);
return getDataTable(result.getList());
}
@GetMapping("/create")
public String create() {
return prefix + "/create";
}
@Log(title = "创建退款原因", businessType = BusinessType.INSERT)
@RequiresPermissions("kylin:refundReasons:create")
@PostMapping("create")
@ResponseBody
public AjaxResult createSave(OrderRefundReasonParam param) {
Boolean res = refundReasonAdminService.create(param);
return res ? success() : error("添加退款原因失败");
}
@GetMapping("/update/{reasonId}")
public String update(@PathVariable("reasonId") String reasonId, ModelMap mmap) {
OrderRefundReasonVo vo = refundReasonAdminService.detail(reasonId);
if (vo == null || (vo.getIsApplied() != null && vo.getIsApplied() == 1)) {
mmap.put("errorMsg", "应用中的退款原因不可编辑,请先取消应用");
}
mmap.put("RefundReasonVo", vo);
return prefix + "/update";
}
@Log(title = "修改退款原因", businessType = BusinessType.UPDATE)
@RequiresPermissions("kylin:refundReasons:update")
@PostMapping("update")
@ResponseBody
public AjaxResult updateSave(OrderRefundReasonParam param) {
Boolean res = refundReasonAdminService.update(param);
return res ? success() : error("修改失败(应用中不可编辑,或数据不存在)");
}
@Log(title = "删除退款原因", businessType = BusinessType.DELETE)
@RequiresPermissions("kylin:refundReasons:delete")
@PostMapping("delete")
@ResponseBody
public AjaxResult delete(OrderRefundReasonParam param) {
List<String> ids = param.getIds();
Boolean result = refundReasonAdminService.delete(ids);
return result ? success("删除成功") : error("删除失败(应用中的原因需先取消应用)");
}
@Log(title = "应用/取消应用退款原因", businessType = BusinessType.UPDATE)
@RequiresPermissions("kylin:refundReasons:apply")
@PutMapping("apply")
@ResponseBody
public AjaxResult apply(@RequestParam("reasonId") String reasonId,
@RequestParam("isApplied") Integer isApplied) {
Boolean result = refundReasonAdminService.apply(reasonId, isApplied);
return result ? success() : error("操作失败");
}
/**
* 已应用列表(供申请页下拉,走缓存)
*/
@RequiresPermissions(value = {"kylin:refund:apply", "kylin:refundBatch:apply", "kylin:refundReasons:list"}, logical = Logical.OR)
@GetMapping("appliedList")
@ResponseBody
public AjaxResult appliedList() {
List<OrderRefundReasonVo> list = refundReasonAdminService.listApplied();
return success(list);
}
}
......@@ -34,9 +34,17 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label layui-required is-required">备注</label>
<label class="col-sm-2 control-label layui-required is-required">退款原因</label>
<div class="col-sm-10">
<textarea name="reason" maxlength="500" class="form-control layui-required" rows="3"></textarea>
<select name="reasonId" id="reasonId" class="form-control layui-required" required>
<option value="">请选择退款原因</option>
</select>
</div>
</div>
<div class="form-group" id="reasonDetailGroup" style="display:none;">
<label class="col-sm-2 control-label layui-required is-required">补充说明:</label>
<div class="col-sm-10">
<textarea name="reasonDetail" id="reasonDetail" maxlength="200" class="form-control" rows="3"></textarea>
</div>
</div>
</form>
......@@ -44,6 +52,38 @@
<th:block th:include="include :: footer"/>
<script type="text/javascript">
var prefix = ctx + "kylin/refund";
var reasonPrefix = ctx + "kylin/refundReasons";
function loadAppliedReasons() {
$.ajax({
url: reasonPrefix + "/appliedList",
type: "get",
dataType: "json",
success: function (result) {
if (result.code == 0 && result.data) {
var $sel = $("#reasonId");
$sel.find("option:not(:first)").remove();
for (var i = 0; i < result.data.length; i++) {
var item = result.data[i];
$sel.append('<option value="' + item.reasonId + '" data-need-detail="' + item.needDetail + '" data-placeholder="' + (item.placeholder || '') + '">' + item.name + '</option>');
}
}
}
});
}
function onReasonChange() {
var $opt = $("#reasonId option:selected");
var needDetail = $opt.data("need-detail");
var placeholder = $opt.data("placeholder") || "请填写补充说明";
if (needDetail == 1) {
$("#reasonDetailGroup").show();
$("#reasonDetail").attr("placeholder", placeholder);
} else {
$("#reasonDetailGroup").hide();
$("#reasonDetail").val("");
}
}
$("#form-post-add").validate({
onkeyup: false,
......@@ -174,7 +214,8 @@
});
function submitHandler() {
var reason = $('#form-post-add').find("[name='reason']").val();
var reasonId = $('#form-post-add').find("[name='reasonId']").val();
var reasonDetail = $('#form-post-add').find("[name='reasonDetail']").val();
var orderTicketsId = $('#form-post-add').find("[name='orderTicketsId']").val();
var rows = $.common.isEmpty(table.options.uniqueId) ? $.table.selectFirstColumns() : $.table.selectColumns(table.options.uniqueId);
if (!orderTicketsId) {
......@@ -185,8 +226,13 @@
layer.msg("请选择需要退款的入场人订单");
return false;
}
if (!reason) {
layer.msg("请填写备注");
if (!reasonId) {
layer.msg("请选择退款原因");
return false;
}
var $opt = $("#reasonId option:selected");
if ($opt.data("need-detail") == 1 && !$.common.trim(reasonDetail)) {
layer.msg("请填写补充说明");
return false;
}
if ($.validate.form()) {
......@@ -204,6 +250,11 @@
}
}
$(function () {
loadAppliedReasons();
$("#reasonId").on("change", onReasonChange);
});
</script>
</body>
</html>
......@@ -147,7 +147,7 @@
<div class="form-control-static" th:text="${KylinOrderRefundsAdminVo.applicantName}"></div>
<label class="col-sm-2 control-label">申请时间:</label>
<div class="form-control-static" th:text="${KylinOrderRefundsAdminVo.applicantAt}"></div>
<label class="col-sm-2 control-label">申请备注</label>
<label class="col-sm-2 control-label">退款原因</label>
<div class="form-control-static" th:text="${KylinOrderRefundsAdminVo.reason}"></div>
</div>
<div class="form-group">
......
......@@ -368,6 +368,19 @@
field: 'priceExpress',
title: '退快递费金额'
},
{
field: 'reason',
title: '退款原因',
formatter: function (value) {
if (!value) {
return '';
}
if (value.length > 30) {
return '<span title="' + value.replace(/"/g, '&quot;') + '">' + value.substring(0, 30) + '...</span>';
}
return value;
}
},
{
field: 'statusName',
title: '退款状态'
......
......@@ -27,9 +27,17 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label layui-required is-required">备注</label>
<label class="col-sm-3 control-label layui-required is-required">退款原因</label>
<div class="col-sm-8">
<textarea name="reason" maxlength="500" class="form-control layui-required" rows="3"></textarea>
<select name="reasonId" id="reasonId" class="form-control layui-required" required>
<option value="">请选择退款原因</option>
</select>
</div>
</div>
<div class="form-group" id="reasonDetailGroup" style="display:none;">
<label class="col-sm-3 control-label layui-required is-required">补充说明:</label>
<div class="col-sm-8">
<textarea name="reasonDetail" id="reasonDetail" maxlength="200" class="form-control" rows="3"></textarea>
</div>
</div>
</form>
......@@ -37,7 +45,38 @@
<th:block th:include="include :: footer" />
<script type="text/javascript">
var prefix = ctx + "kylin/refundBatch";
var reasonPrefix = ctx + "kylin/refundReasons";
function loadAppliedReasons() {
$.ajax({
url: reasonPrefix + "/appliedList",
type: "get",
dataType: "json",
success: function (result) {
if (result.code == 0 && result.data) {
var $sel = $("#reasonId");
$sel.find("option:not(:first)").remove();
for (var i = 0; i < result.data.length; i++) {
var item = result.data[i];
$sel.append('<option value="' + item.reasonId + '" data-need-detail="' + item.needDetail + '" data-placeholder="' + (item.placeholder || '') + '">' + item.name + '</option>');
}
}
}
});
}
function onReasonChange() {
var $opt = $("#reasonId option:selected");
var needDetail = $opt.data("need-detail");
var placeholder = $opt.data("placeholder") || "请填写补充说明";
if (needDetail == 1) {
$("#reasonDetailGroup").show();
$("#reasonDetail").attr("placeholder", placeholder);
} else {
$("#reasonDetailGroup").hide();
$("#reasonDetail").val("");
}
}
$("#form-post-add").validate({
onkeyup: false,
rules:{
......@@ -136,14 +175,20 @@
});
function submitHandler() {
var reason = $('#form-post-add').find("[name='reason']").val();
var reasonId = $('#form-post-add').find("[name='reasonId']").val();
var reasonDetail = $('#form-post-add').find("[name='reasonDetail']").val();
var targetId = $('#form-post-add').find("[name='targetId']").val();
if (!targetId) {
layer.msg("演出id有误");
return false;
}
if (!reason) {
layer.msg("请填写备注");
if (!reasonId) {
layer.msg("请选择退款原因");
return false;
}
var $opt = $("#reasonId option:selected");
if ($opt.data("need-detail") == 1 && !$.common.trim(reasonDetail)) {
layer.msg("请填写补充说明");
return false;
}
if ($.validate.form()) {
......@@ -151,6 +196,11 @@
$.operate.save(prefix + "/apply", data);
}
}
$(function () {
loadAppliedReasons();
$("#reasonId").on("change", onReasonChange);
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('添加退款原因')"/>
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-reason-add">
<div class="form-group">
<label class="col-sm-3 control-label is-required">退款理由名称:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="name" id="name"
maxlength="100" placeholder="展示给用户的文案" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">是否需补充说明:</label>
<div class="col-sm-8">
<div class="radio check-box">
<label>
<input type="radio" value="0" name="needDetail" checked> 否(仅选择)
</label>
</div>
<div class="radio check-box">
<label>
<input type="radio" value="1" name="needDetail"> 是(选择后需输入)
</label>
</div>
</div>
</div>
<div class="form-group" id="placeholderGroup" style="display:none;">
<label class="col-sm-3 control-label">补充输入提示:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="placeholder" id="placeholder"
maxlength="200" placeholder="二级输入框 placeholder">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序:</label>
<div class="col-sm-8">
<input class="form-control" type="number" name="sort" id="sort" value="0">
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 数值越大越靠前</span>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer"/>
<script type="text/javascript">
var prefix = ctx + "kylin/refundReasons";
function submitHandler() {
var name = $('#name').val().trim();
if (!name) {
$.modal.alertWarning("请填写退款理由名称");
return false;
}
if ($.validate.form()) {
var data = $('#form-reason-add').serializeArray();
$.operate.save(prefix + "/create", data);
}
}
$(function () {
$('input[name="needDetail"]').on('change', function () {
if ($(this).val() == '1') {
$('#placeholderGroup').show();
} else {
$('#placeholderGroup').hide();
$('#placeholder').val('');
}
});
});
</script>
</body>
</html>
<!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('退款原因列表')"/>
<style>
.btn-disabled-edit {
color: #ccc !important;
cursor: not-allowed !important;
pointer-events: none;
}
</style>
</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>
<label>名称:</label>
<input type="text" name="name"/>
</li>
<li>
<label>应用状态:</label>
<select name="isApplied">
<option value="">全部</option>
<option value="1">应用中</option>
<option value="0">未应用</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="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="kylin:refundReasons:create">
<i class="fa fa-plus"></i> 添加
</a>
</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 updateFlag = [[${@permission.hasPermi('kylin:refundReasons:update')}]];
var removeFlag = [[${@permission.hasPermi('kylin:refundReasons:delete')}]];
var applyFlag = [[${@permission.hasPermi('kylin:refundReasons:apply')}]];
var prefix = ctx + "kylin/refundReasons";
$(function () {
var options = {
url: prefix + "/list",
updateUrl: prefix + "/update/{id}",
createUrl: prefix + "/create",
removeUrl: prefix + "/delete",
modalName: "退款原因",
columns: [
{
field: 'name',
title: '退款理由'
},
{
field: 'needDetail',
title: '需补充说明',
formatter: function (value) {
return value == 1 ? '是' : '否';
}
},
{
field: 'placeholder',
title: '补充提示文案'
},
{
field: 'isApplied',
title: '应用状态',
formatter: function (value) {
if (value == 1) {
return '<span class="badge badge-primary">应用中</span>';
}
return '<span class="badge badge-warning">未应用</span>';
}
},
{
field: 'sort',
title: '排序'
},
{
field: 'updatedAt',
title: '更新时间',
formatter: function (value) {
if (!value) {
return '';
}
var date = new Date(value);
return date.getFullYear() + '-' +
String(date.getMonth() + 1).padStart(2, '0') + '-' +
String(date.getDate()).padStart(2, '0') + ' ' +
String(date.getHours()).padStart(2, '0') + ':' +
String(date.getMinutes()).padStart(2, '0') + ':' +
String(date.getSeconds()).padStart(2, '0');
}
},
{
title: '操作',
align: 'center',
formatter: function (value, row) {
var actions = [];
if (row.isApplied == 1) {
actions.push('<a class="btn btn-success btn-xs btn-disabled-edit" href="javascript:void(0)" title="请先取消应用"><i class="fa fa-edit"></i>编辑</a> ');
actions.push('<a class="btn btn-warning btn-xs ' + applyFlag + '" href="javascript:void(0)" onclick="toggleApply(\'' + row.reasonId + '\', 0)"><i class="fa fa-ban"></i>取消应用</a> ');
} else {
actions.push('<a class="btn btn-success btn-xs ' + updateFlag + '" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.reasonId + '\')"><i class="fa fa-edit"></i>编辑</a> ');
actions.push('<a class="btn btn-primary btn-xs ' + applyFlag + '" href="javascript:void(0)" onclick="toggleApply(\'' + row.reasonId + '\', 1)"><i class="fa fa-check"></i>应用</a> ');
actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="removeReason(\'' + row.reasonId + '\')"><i class="fa fa-remove"></i>删除</a> ');
}
return actions.join('');
}
}]
};
$.table.init(options);
});
function removeReason(id) {
$.operate.remove(id, "确认删除该退款原因吗?");
}
function toggleApply(reasonId, isApplied) {
var title = isApplied == 1 ? '应用' : '取消应用';
$.modal.confirm('确定要' + title + '该退款原因吗?', function () {
$.ajax({
type: 'put',
url: prefix + '/apply',
data: {reasonId: reasonId, isApplied: isApplied},
success: function (e) {
if (e.code == 0) {
$.modal.msgSuccess(title + '成功');
$("button[name=refresh]").click();
} else {
$.modal.alertError(e.msg || (title + '失败'));
}
}
});
});
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('修改退款原因')"/>
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<div th:if="${errorMsg != null}" class="alert alert-warning" th:text="${errorMsg}"></div>
<form class="form-horizontal m" id="form-reason-edit" th:if="${RefundReasonVo != null and RefundReasonVo.isApplied != 1}">
<input type="hidden" name="reasonId" th:value="${RefundReasonVo.reasonId}">
<div class="form-group">
<label class="col-sm-3 control-label is-required">退款理由名称:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="name" id="name"
maxlength="100" placeholder="展示给用户的文案"
th:value="${RefundReasonVo.name}" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">是否需补充说明:</label>
<div class="col-sm-8">
<div class="radio check-box">
<label>
<input type="radio" value="0" name="needDetail"
th:checked="${RefundReasonVo.needDetail == null or RefundReasonVo.needDetail == 0}"> 否(仅选择)
</label>
</div>
<div class="radio check-box">
<label>
<input type="radio" value="1" name="needDetail"
th:checked="${RefundReasonVo.needDetail == 1}"> 是(选择后需输入)
</label>
</div>
</div>
</div>
<div class="form-group" id="placeholderGroup"
th:style="${RefundReasonVo.needDetail == 1} ? '' : 'display:none;'">
<label class="col-sm-3 control-label">补充输入提示:</label>
<div class="col-sm-8">
<input class="form-control" type="text" name="placeholder" id="placeholder"
maxlength="200" placeholder="二级输入框 placeholder"
th:value="${RefundReasonVo.placeholder}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序:</label>
<div class="col-sm-8">
<input class="form-control" type="number" name="sort" id="sort"
th:value="${RefundReasonVo.sort}">
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 数值越大越靠前</span>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer"/>
<script type="text/javascript">
var prefix = ctx + "kylin/refundReasons";
function submitHandler() {
if ($('#form-reason-edit').length === 0) {
$.modal.alertWarning("应用中的退款原因不可编辑");
return false;
}
var name = $('#name').val().trim();
if (!name) {
$.modal.alertWarning("请填写退款理由名称");
return false;
}
if ($.validate.form()) {
var data = $('#form-reason-edit').serializeArray();
$.operate.save(prefix + "/update", data);
}
}
$(function () {
$('input[name="needDetail"]').on('change', function () {
if ($(this).val() == '1') {
$('#placeholderGroup').show();
} else {
$('#placeholderGroup').hide();
$('#placeholder').val('');
}
});
});
</script>
</body>
</html>
package com.liquidnet.client.admin.zhengzai.kylin.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.zhengzai.kylin.utils.DataUtils;
import com.liquidnet.commons.lang.util.BeanUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonParam;
import com.liquidnet.service.kylin.dto.param.OrderRefundReasonSearchParam;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import com.liquidnet.service.kylin.mapper.KylinOrderRefundReasonsMapper;
import com.liquidnet.service.kylin.service.admin.IOrderRefundReasonAdminService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 退款原因配置管理
*/
@Slf4j
@Service
public class KylinOrderRefundReasonAdminServiceImpl
extends ServiceImpl<KylinOrderRefundReasonsMapper, KylinOrderRefundReasons>
implements IOrderRefundReasonAdminService {
@Autowired
private KylinOrderRefundReasonsMapper reasonsMapper;
@Autowired
private DataUtils dataUtils;
@Override
public Boolean create(OrderRefundReasonParam param) {
try {
if (StringUtils.isBlank(param.getName())) {
return false;
}
String reasonId = IDGenerator.nextSnowId();
LocalDateTime now = LocalDateTime.now();
KylinOrderRefundReasons entity = param.getFields(reasonId, now);
// 新建默认未应用
entity.setIsApplied(0);
reasonsMapper.insert(entity);
refreshAppliedCache();
return true;
} catch (Exception e) {
log.error("创建退款原因失败", e);
return false;
}
}
@Override
public Boolean update(OrderRefundReasonParam param) {
try {
if (StringUtils.isBlank(param.getReasonId()) || StringUtils.isBlank(param.getName())) {
return false;
}
KylinOrderRefundReasons existing = reasonsMapper.selectOne(
new QueryWrapper<KylinOrderRefundReasons>().eq("reason_id", param.getReasonId())
);
if (existing == null) {
return false;
}
// 应用中不可编辑
if (existing.getIsApplied() != null && existing.getIsApplied() == 1) {
return false;
}
KylinOrderRefundReasons entity = param.getFields(null, null);
// 不允许通过 update 直接改应用态
entity.setIsApplied(null);
entity.setReasonId(null);
entity.setCreatedAt(null);
entity.setUpdatedAt(LocalDateTime.now());
reasonsMapper.update(entity, new UpdateWrapper<KylinOrderRefundReasons>()
.eq("reason_id", param.getReasonId()));
refreshAppliedCache();
return true;
} catch (Exception e) {
log.error("修改退款原因失败", e);
return false;
}
}
@Override
public OrderRefundReasonVo detail(String reasonId) {
KylinOrderRefundReasons data = reasonsMapper.selectOne(
new QueryWrapper<KylinOrderRefundReasons>().eq("reason_id", reasonId)
);
if (data == null) {
return null;
}
return toVo(data);
}
@Override
public PageInfo<KylinOrderRefundReasons> reasonList(OrderRefundReasonSearchParam param) {
try {
PageHelper.startPage(param.getPageNum(), param.getPageSize());
Map<String, Object> paramMap = BeanUtil.convertBeanToMap(param);
List<KylinOrderRefundReasons> list = reasonsMapper.searchList(paramMap);
if (list != null) {
for (KylinOrderRefundReasons item : list) {
item.setIsAppliedName(formatAppliedName(item.getIsApplied()));
}
}
return new PageInfo<>(list);
} catch (Exception e) {
log.error("获取退款原因列表失败", e);
return new PageInfo<>();
}
}
@Override
public Boolean delete(List<String> reasonIds) {
try {
if (reasonIds == null || reasonIds.isEmpty()) {
return false;
}
List<KylinOrderRefundReasons> list = reasonsMapper.selectList(
new QueryWrapper<KylinOrderRefundReasons>().in("reason_id", reasonIds)
);
for (KylinOrderRefundReasons item : list) {
if (item.getIsApplied() != null && item.getIsApplied() == 1) {
return false;
}
}
reasonsMapper.delete(new QueryWrapper<KylinOrderRefundReasons>().in("reason_id", reasonIds));
refreshAppliedCache();
return true;
} catch (Exception e) {
log.error("删除退款原因失败", e);
return false;
}
}
@Override
public Boolean apply(String reasonId, Integer isApplied) {
try {
if (StringUtils.isBlank(reasonId) || isApplied == null || (isApplied != 0 && isApplied != 1)) {
return false;
}
KylinOrderRefundReasons existing = reasonsMapper.selectOne(
new QueryWrapper<KylinOrderRefundReasons>().eq("reason_id", reasonId)
);
if (existing == null) {
return false;
}
KylinOrderRefundReasons update = new KylinOrderRefundReasons();
update.setIsApplied(isApplied);
update.setUpdatedAt(LocalDateTime.now());
reasonsMapper.update(update, new UpdateWrapper<KylinOrderRefundReasons>().eq("reason_id", reasonId));
refreshAppliedCache();
return true;
} catch (Exception e) {
log.error("应用/取消应用退款原因失败", e);
return false;
}
}
@Override
public List<OrderRefundReasonVo> listApplied() {
List<OrderRefundReasonVo> cached = dataUtils.getOrderRefundReasonsApplied();
if (cached != null) {
return cached;
}
return refreshAppliedCache();
}
private List<OrderRefundReasonVo> refreshAppliedCache() {
List<KylinOrderRefundReasons> list = reasonsMapper.listApplied();
List<OrderRefundReasonVo> voList = new ArrayList<>();
if (list != null) {
for (KylinOrderRefundReasons item : list) {
voList.add(toVo(item));
}
}
dataUtils.setOrderRefundReasonsApplied(voList);
return voList;
}
private OrderRefundReasonVo toVo(KylinOrderRefundReasons data) {
OrderRefundReasonVo vo = new OrderRefundReasonVo();
BeanUtils.copyProperties(data, vo);
vo.setIsAppliedName(formatAppliedName(data.getIsApplied()));
if (data.getCreatedAt() != null) {
vo.setCreatedAt(DateUtil.Formatter.yyyyMMddHHmmss.format(data.getCreatedAt()));
}
if (data.getUpdatedAt() != null) {
vo.setUpdatedAt(DateUtil.Formatter.yyyyMMddHHmmss.format(data.getUpdatedAt()));
}
return vo;
}
private String formatAppliedName(Integer isApplied) {
if (isApplied != null && isApplied == 1) {
return "应用中";
}
return "未应用";
}
}
......@@ -31,6 +31,7 @@ import com.liquidnet.service.kylin.dto.vo.returns.RefundOrderDetailsVo;
import com.liquidnet.service.kylin.entity.*;
import com.liquidnet.service.kylin.mapper.*;
import com.liquidnet.service.kylin.service.IKylinOrderRefundsService;
import com.liquidnet.service.kylin.utils.OrderRefundReasonUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
......@@ -89,6 +90,9 @@ public class KylinOrderRefundsServiceImpl extends ServiceImpl<KylinOrderRefundsM
@Autowired
private KylinOrderRefundPicMapper kylinOrderRefundPicMapper;
@Autowired
private KylinOrderRefundReasonAdminServiceImpl refundReasonAdminService;
@Autowired
MongoTemplate mongoTemplate;
@Autowired
......@@ -96,6 +100,10 @@ public class KylinOrderRefundsServiceImpl extends ServiceImpl<KylinOrderRefundsM
public ResponseDto refundApply(RefundApplyParam refundApplyParam, String authId, String authName) {
try {
ResponseDto reasonCheck = fillRefundReason(refundApplyParam);
if (reasonCheck != null) {
return reasonCheck;
}
String orderTicketsId = refundApplyParam.getOrderTicketsId();
List<String> ticketEntityIds = refundApplyParam.getTicketEntityIds();
List<BigDecimal> entitiesPrice = refundApplyParam.getEntitiesPrice();
......@@ -574,4 +582,21 @@ public class KylinOrderRefundsServiceImpl extends ServiceImpl<KylinOrderRefundsM
}
return orderCode;
}
/**
* 校验并填充退款原因快照(从已应用配置缓存)
*
* @return 失败时返回 ResponseDto;成功返回 null
*/
private ResponseDto fillRefundReason(RefundApplyParam param) {
OrderRefundReasonUtils.ResolvedRefundReason resolved = OrderRefundReasonUtils.resolve(
param.getReasonId(), param.getReasonDetail(), refundReasonAdminService.listApplied());
if (!resolved.isSuccess()) {
return ResponseDto.failure(resolved.getErrorMsg());
}
param.setReasonId(resolved.getReasonId());
param.setReasonDetail(resolved.getReasonDetail());
param.setReason(resolved.getReasonSnapshot());
return null;
}
}
......@@ -52,7 +52,7 @@ public class KylinRefundExecuteServiceImpl {
private KylinOrderRefundsServiceImpl kylinOrderRefundsServiceImpl;
@Async
public void refundBatchApply(String targetId, String refundBatchId, String reason, String authId, String authName) {
public void refundBatchApply(String targetId, String refundBatchId, String reason, String reasonId, String reasonDetail, String authId, String authName) {
RefundApplyParam refundApplyParam = new RefundApplyParam();
int count;
int limitNum = 10;
......@@ -70,6 +70,8 @@ public class KylinRefundExecuteServiceImpl {
refundApplyParam.setOrderRefundBatchesId(refundBatchId);
refundApplyParam.setReason(reason);
refundApplyParam.setReasonId(reasonId);
refundApplyParam.setReasonDetail(reasonDetail);
refundApplyParam.setOrderTicketsId(order.getOrderTicketsId());
refundApplyParam.setTicketEntityIds(ticketEntityIds);
if (null != order.getPriceExpress()) {
......
......@@ -21,6 +21,7 @@ import com.liquidnet.service.kylin.mapper.KylinOrderRefundBatchesMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderRefundsMapper;
import com.liquidnet.service.kylin.mapper.KylinOrderTicketsMapper;
import com.liquidnet.service.kylin.mapper.KylinPerformancesMapper;
import com.liquidnet.service.kylin.utils.OrderRefundReasonUtils;
import com.liquidnet.service.dragon.support.WepayAppletPaySupport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
......@@ -65,7 +66,21 @@ public class KylinRefundPerformancesAdminServiceImpl {
@Autowired
RedisDataSourceUtil redisDataSourceUtil;
@Autowired
private KylinOrderRefundReasonAdminServiceImpl refundReasonAdminService;
public ResponseDto refundBatchApply(RefundBatchApplyParam refundBatchApplyParam) {
OrderRefundReasonUtils.ResolvedRefundReason resolved = OrderRefundReasonUtils.resolve(
refundBatchApplyParam.getReasonId(),
refundBatchApplyParam.getReasonDetail(),
refundReasonAdminService.listApplied());
if (!resolved.isSuccess()) {
return ResponseDto.failure(resolved.getErrorMsg());
}
refundBatchApplyParam.setReasonId(resolved.getReasonId());
refundBatchApplyParam.setReasonDetail(resolved.getReasonDetail());
refundBatchApplyParam.setReason(resolved.getReasonSnapshot());
String targetId = refundBatchApplyParam.getTargetId();
// 查询需要退的金额和数量
String[] paymentTypeAlipay = WepayAppletPaySupport.alipayPaymentTypesForRefundStatis();
......@@ -122,7 +137,8 @@ public class KylinRefundPerformancesAdminServiceImpl {
// 开始执行批量提交审核 分批处理退款申请
String authId = ShiroUtils.getUserId().toString();
String authName = ShiroUtils.getLoginName();
kylinRefundExecuteServiceImpl.refundBatchApply(targetId, refundBatchId, refundBatchApplyParam.getReason(), authId, authName);
kylinRefundExecuteServiceImpl.refundBatchApply(targetId, refundBatchId, refundBatchApplyParam.getReason(),
refundBatchApplyParam.getReasonId(), refundBatchApplyParam.getReasonDetail(), authId, authName);
return ResponseDto.success();
} else {
return ResponseDto.failure("操作失败");
......
......@@ -172,6 +172,8 @@ public class KylinRefundsStatusServiceImpl {
kylinOrderRefunds.setApplicantName(authName);
kylinOrderRefunds.setApplicantAt(LocalDateTime.now());
kylinOrderRefunds.setReason(reason);
kylinOrderRefunds.setReasonId(refundApplyParam.getReasonId());
kylinOrderRefunds.setReasonDetail(refundApplyParam.getReasonDetail());
kylinOrderRefunds.setCreatedAt(LocalDateTime.now());
int rows = kylinOrderRefundsMapper.insert(kylinOrderRefunds);
// 添加缓存
......
......@@ -10,6 +10,7 @@ import com.liquidnet.service.base.constant.RedisKeyExpireConst;
import com.liquidnet.service.candy.param.BackCouponParam;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.vo.KylinExpressModuleVo;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.dto.vo.admin.OrderRefundAddress;
import com.liquidnet.service.kylin.dto.vo.admin.OrderRefundPoundage;
import com.liquidnet.service.kylin.dto.vo.express.KylinOrderExpressVo;
......@@ -464,6 +465,23 @@ public class DataUtils {
redisDataSourceUtil.getRedisKylinUtil().set(redisKey, voList);
}
public void setOrderRefundReasonsApplied(List<OrderRefundReasonVo> voList) {
redisDataSourceUtil.getRedisKylinUtil().set(KylinRedisConst.KYLIN_ORDER_REFUND_REASONS_APPLIED, voList);
}
@SuppressWarnings("unchecked")
public List<OrderRefundReasonVo> getOrderRefundReasonsApplied() {
Object obj = redisDataSourceUtil.getRedisKylinUtil().get(KylinRedisConst.KYLIN_ORDER_REFUND_REASONS_APPLIED);
if (obj == null) {
return null;
}
return (List<OrderRefundReasonVo>) obj;
}
public void delOrderRefundReasonsApplied() {
redisDataSourceUtil.getRedisKylinUtil().del(KylinRedisConst.KYLIN_ORDER_REFUND_REASONS_APPLIED);
}
/**
* 获取 票种快递票模板关联vo [第三方非线上]
*
......
package com.liquidnet.service.kylin.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 演出票退款原因配置表
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class KylinOrderRefundReasons implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Integer mid;
/**
* 业务ID
*/
private String reasonId;
/**
* 退款理由名称
*/
private String name;
/**
* 是否需要二级补充输入 0否 1是
*/
private Integer needDetail;
/**
* 二级输入 placeholder
*/
private String placeholder;
/**
* 是否已应用 0未应用 1应用中
*/
private Integer isApplied;
/**
* 排序,越大越靠前
*/
private Integer sort;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@TableField(exist = false)
private String isAppliedName;
private static final KylinOrderRefundReasons OBJ = new KylinOrderRefundReasons();
public static KylinOrderRefundReasons getNew() {
try {
return (KylinOrderRefundReasons) OBJ.clone();
} catch (CloneNotSupportedException e) {
return new KylinOrderRefundReasons();
}
}
}
......@@ -106,10 +106,20 @@ public class KylinOrderRefunds implements Serializable, Cloneable{
private LocalDateTime applicantAt;
/**
* 退款原因
* 退款原因(展示快照)
*/
private String reason;
/**
* 退款原因配置ID
*/
private String reasonId;
/**
* 退款原因二级补充说明
*/
private String reasonDetail;
/**
* 审核人id
*/
......
package com.liquidnet.service.kylin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import java.util.List;
import java.util.Map;
/**
* 演出票退款原因配置 Mapper
*/
public interface KylinOrderRefundReasonsMapper extends BaseMapper<KylinOrderRefundReasons> {
List<KylinOrderRefundReasons> searchList(Map<String, Object> params);
List<KylinOrderRefundReasons> listApplied();
}
<?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.liquidnet.service.kylin.mapper.KylinOrderRefundReasonsMapper">
<select id="searchList" parameterType="java.util.Map" resultType="com.liquidnet.service.kylin.entity.KylinOrderRefundReasons">
SELECT
mid,
reason_id,
name,
need_detail,
placeholder,
is_applied,
sort,
created_at,
updated_at
FROM kylin_order_refund_reasons
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="isApplied != null and isApplied != ''">
AND is_applied = #{isApplied}
</if>
</where>
ORDER BY sort DESC, mid DESC
</select>
<select id="listApplied" resultType="com.liquidnet.service.kylin.entity.KylinOrderRefundReasons">
SELECT
mid,
reason_id,
name,
need_detail,
placeholder,
is_applied,
sort,
created_at,
updated_at
FROM kylin_order_refund_reasons
WHERE is_applied = 1
ORDER BY sort DESC, mid DESC
</select>
</mapper>
......@@ -793,6 +793,28 @@ alter table kylin_order_refunds add ticket_audit_name varchar(100) NOT NULL DE
alter table kylin_order_refunds add ticket_audit_at timestamp NULL DEFAULT NULL COMMENT '票务审核时间' after ticket_audit_name;
alter table kylin_order_refunds add ticket_audit_remark varchar(200) NOT NULL DEFAULT '' COMMENT '票务审核备至' after ticket_audit_at;
alter table kylin_order_refunds add reason_id varchar(32) DEFAULT NULL COMMENT '退款原因配置ID' after reason;
alter table kylin_order_refunds add reason_detail varchar(200) DEFAULT NULL COMMENT '退款原因二级补充说明' after reason_id;
# 演出票退款原因配置表
drop TABLE if exists `kylin_order_refund_reasons`;
CREATE TABLE `kylin_order_refund_reasons`
(
`mid` int(11) NOT NULL AUTO_INCREMENT,
`reason_id` varchar(32) NOT NULL DEFAULT '' COMMENT '业务ID',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '退款理由名称',
`need_detail` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否需要二级补充输入 0否 1是',
`placeholder` varchar(200) NOT NULL DEFAULT '' COMMENT '二级输入placeholder',
`is_applied` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已应用 0未应用 1应用中',
`sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序,越大越靠前',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_reason_id` (`reason_id`),
KEY `idx_is_applied_sort` (`is_applied`, `sort`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='演出票退款原因配置';
drop TABLE if exists `kylin_order_refund_entities`;
CREATE TABLE `kylin_order_refund_entities`
(
......
package com.liquidnet.service.kylin.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.service.impl.KylinOrderRefundReasonServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 退款原因(App/H5)
*/
@Api(tags = "前端-退款原因")
@RestController
@RequestMapping("orderRefundReason")
@Validated
public class KylinOrderRefundReasonController {
@Autowired
private KylinOrderRefundReasonServiceImpl refundReasonService;
@GetMapping("appliedList")
@ApiOperation("已应用退款理由列表")
@ApiResponse(code = 200, message = "接口返回对象参数")
public ResponseDto<List<OrderRefundReasonVo>> appliedList() {
return ResponseDto.success(refundReasonService.listApplied());
}
}
......@@ -63,7 +63,9 @@ public class KylinOrderTicketsRefundController {
@ApiImplicitParams({
@ApiImplicitParam(type = "form", dataType = "String", name = "orderId", value = "订单ID", required = true),
@ApiImplicitParam(type = "form", dataType = "String", name = "orderTicketEntitiesId", value = "订单入场人ID", required = true),
@ApiImplicitParam(type = "form", dataType = "String", name = "reason", value = "退款申请备注", required = false),
@ApiImplicitParam(type = "form", dataType = "String", name = "reasonId", value = "退款原因配置ID", required = true),
@ApiImplicitParam(type = "form", dataType = "String", name = "reasonDetail", value = "退款原因补充说明"),
@ApiImplicitParam(type = "form", dataType = "String", name = "reason", value = "退款申请备注(兼容旧版,优先使用reasonId)", required = false),
@ApiImplicitParam(type = "form", dataType = "String", name = "picList", value = "图片"),
@ApiImplicitParam(type = "form", dataType = "String", name = "ticketNum", value = "纸质票号"),
@ApiImplicitParam(type = "form", dataType = "Integer", name = "sendExpressType", value = "快递方式 1上门取件 2自主发货"),
......@@ -76,7 +78,9 @@ public class KylinOrderTicketsRefundController {
public ResponseDto sendOrderRefund(
@RequestParam(value = "orderId", required = true) @NotNull(message = "订单ID不能为空") String orderId,
@RequestParam(value = "orderTicketEntitiesId", required = true) @NotNull(message = "票单ID不能为空") String orderTicketEntitiesId,
@RequestParam(value = "reason", required = false) @NotNull(message = "退款原因不能为空") String reason,
@RequestParam(value = "reasonId", required = false) String reasonId,
@RequestParam(value = "reasonDetail", required = false) String reasonDetail,
@RequestParam(value = "reason", required = false) String reason,
@RequestParam(value = "picList", required = false) String picList,
@RequestParam(value = "ticketNum", required = false) String ticketNum,
......@@ -88,9 +92,8 @@ public class KylinOrderTicketsRefundController {
@RequestParam(value = "sendExpressAddress", required = false) String sendExpressAddress,
@RequestParam(value = "appointmentTime", required = false) String appointmentTime
) {
// 退款
return kylinOrderTicketsRefundService.sendOrderRefund(
orderId, orderTicketEntitiesId, reason, picList, ticketNum,
orderId, orderTicketEntitiesId, reasonId, reasonDetail, reason, picList, ticketNum,
sendExpressType, expressNumber, expressContacts, expressPhone, sendExpressAddress, appointmentTime
);
}
......
package com.liquidnet.service.kylin.service.impl;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.service.kylin.constant.KylinRedisConst;
import com.liquidnet.service.kylin.dto.vo.OrderRefundReasonVo;
import com.liquidnet.service.kylin.entity.KylinOrderRefundReasons;
import com.liquidnet.service.kylin.mapper.KylinOrderRefundReasonsMapper;
import com.liquidnet.service.kylin.service.IKylinOrderRefundReasonService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 退款原因(App/H5)
*/
@Slf4j
@Service
public class KylinOrderRefundReasonServiceImpl implements IKylinOrderRefundReasonService {
@Autowired
private RedisUtil redisUtil;
@Autowired
private KylinOrderRefundReasonsMapper reasonsMapper;
@Override
@SuppressWarnings("unchecked")
public List<OrderRefundReasonVo> listApplied() {
Object cached = redisUtil.get(KylinRedisConst.KYLIN_ORDER_REFUND_REASONS_APPLIED);
if (cached != null) {
return (List<OrderRefundReasonVo>) cached;
}
List<KylinOrderRefundReasons> list = reasonsMapper.listApplied();
List<OrderRefundReasonVo> voList = new ArrayList<>();
if (list != null) {
for (KylinOrderRefundReasons item : list) {
OrderRefundReasonVo vo = new OrderRefundReasonVo();
BeanUtils.copyProperties(item, vo);
voList.add(vo);
}
}
redisUtil.set(KylinRedisConst.KYLIN_ORDER_REFUND_REASONS_APPLIED, voList);
return voList;
}
@Override
public OrderRefundReasonVo getAppliedByReasonId(String reasonId) {
if (StringUtils.isBlank(reasonId)) {
return null;
}
List<OrderRefundReasonVo> list = listApplied();
if (list == null) {
return null;
}
for (OrderRefundReasonVo item : list) {
if (reasonId.equals(item.getReasonId())) {
return item;
}
}
return null;
}
}
......@@ -64,6 +64,8 @@ public class KylinOrderTicketsRefundServiceImpl {
private QueueUtils queueUtils;
@Autowired
private KylinRefundsStatusServiceImpl refundsStatusService;
@Autowired
private KylinOrderRefundReasonServiceImpl refundReasonService;
@Value("${liquidnet.express.shunfeng.custid}")
private String custid;
......@@ -170,7 +172,7 @@ public class KylinOrderTicketsRefundServiceImpl {
}
}
public ResponseDto sendOrderRefund(String orderId, String orderTicketEntitiesId, String reason, String picList, String ticketNum, Integer sendExpressType, String expressNumber, String expressContacts, String expressPhone, String sendExpressAddress, String appointmentTime) {
public ResponseDto sendOrderRefund(String orderId, String orderTicketEntitiesId, String reasonId, String reasonDetail, String reason, String picList, String ticketNum, Integer sendExpressType, String expressNumber, String expressContacts, String expressPhone, String sendExpressAddress, String appointmentTime) {
boolean isLock = dataUtils.setRefundLock(orderTicketEntitiesId);
if (!isLock) {
return ResponseDto.failure("正在处理,请勿重复操作~");
......@@ -178,6 +180,25 @@ public class KylinOrderTicketsRefundServiceImpl {
KylinOrderTicketEntitiesVo orderTicketEntitiesVo = null;
try {
String resolvedReason;
String resolvedReasonId = null;
String resolvedReasonDetail = null;
if (StringUtils.isNotBlank(reasonId)) {
OrderRefundReasonUtils.ResolvedRefundReason resolved = OrderRefundReasonUtils.resolve(
refundReasonService.getAppliedByReasonId(reasonId), reasonDetail);
if (!resolved.isSuccess()) {
return ResponseDto.failure(resolved.getErrorMsg());
}
resolvedReasonId = resolved.getReasonId();
resolvedReasonDetail = resolved.getReasonDetail();
resolvedReason = resolved.getReasonSnapshot();
} else if (StringUtils.isNotBlank(reason)) {
// 兼容旧版自由文本
resolvedReason = OrderRefundReasonUtils.truncate(reason, 200);
} else {
return ResponseDto.failure("请选择退款原因");
}
String uid = CurrentUtil.getCurrentUid();
KylinOrderTicketVo orderTicketVo = dataUtils.getOrderTicketVo(orderId);
List<KylinOrderRefundsVo> kylinOrderRefundsVoBaseList = dataUtils.getOrderRefundVoByOrderId(orderTicketVo.getOrderTicketsId());
......@@ -267,7 +288,7 @@ public class KylinOrderTicketsRefundServiceImpl {
Map token = CurrentUtil.getTokenClaims();
String username = StringUtils.defaultString(((String) token.get("nickname")), "");
String orderRefundId = refundsStatusService.userOrderTicketRefunding(orderTicketVo, refundSinglePrice, priceExpress, priceCharges, orderTicketEntitiesId, reason, picList, ticketNum, uid, username, kylinOrderRefundsVoBaseList.size(), sendExpressType);
String orderRefundId = refundsStatusService.userOrderTicketRefunding(orderTicketVo, refundSinglePrice, priceExpress, priceCharges, orderTicketEntitiesId, resolvedReason, resolvedReasonId, resolvedReasonDetail, picList, ticketNum, uid, username, kylinOrderRefundsVoBaseList.size(), sendExpressType);
if (!orderRefundId.isEmpty()) {
// 退款申请成功 异步去快递下单
if (orderTicketVo.getGetTicketType().equals("express") && sendExpressType > 0) {// 快递票
......
......@@ -56,6 +56,8 @@ public class KylinRefundsStatusServiceImpl {
BigDecimal priceCharges,
String orderEntitiesId,
String reason,
String reasonId,
String reasonDetail,
String picList,
String ticketNum,
String uid,
......@@ -120,6 +122,8 @@ public class KylinRefundsStatusServiceImpl {
kylinOrderRefunds.setApplicantName(username);
kylinOrderRefunds.setApplicantAt(time);
kylinOrderRefunds.setReason(reason);
kylinOrderRefunds.setReasonId(reasonId);
kylinOrderRefunds.setReasonDetail(reasonDetail);
kylinOrderRefunds.setCreatedAt(time);
KylinOrderRefundsVo orderRefundsVo = KylinOrderRefundsVo.getNew();
......@@ -182,7 +186,7 @@ public class KylinRefundsStatusServiceImpl {
kylinOrderRefunds.getOrderRefundsId(), kylinOrderRefunds.getOrderTicketsId(), kylinOrderRefunds.getOrderRefundCode(),
kylinOrderRefunds.getPrice(), kylinOrderRefunds.getPriceExpress(), kylinOrderRefunds.getPriceCharges(), ticketNum, kylinOrderRefunds.getStatus(),
kylinOrderRefunds.getType(), kylinOrderRefunds.getApplicantId(), kylinOrderRefunds.getApplicantName(),
kylinOrderRefunds.getApplicantAt(), kylinOrderRefunds.getReason(), kylinOrderRefunds.getCreatedAt()
kylinOrderRefunds.getApplicantAt(), kylinOrderRefunds.getReason(), kylinOrderRefunds.getReasonId(), kylinOrderRefunds.getReasonDetail(), kylinOrderRefunds.getCreatedAt()
});
sqlsDataD.add(new Object[]{
kylinOrderRefundEntities.getOrderRefundsEntitiesId(), kylinOrderRefundEntities.getOrderRefundsId(), kylinOrderRefundEntities.getRefundPrice(),
......@@ -332,9 +336,9 @@ public class KylinRefundsStatusServiceImpl {
});
sqlsDataC.add(new Object[]{
kylinOrderRefunds.getOrderRefundsId(), kylinOrderRefunds.getOrderTicketsId(), kylinOrderRefunds.getOrderRefundCode(),
kylinOrderRefunds.getPrice(), kylinOrderRefunds.getPriceExpress(), 0, kylinOrderRefunds.getStatus(),
kylinOrderRefunds.getPrice(), kylinOrderRefunds.getPriceExpress(), 0, null, kylinOrderRefunds.getStatus(),
kylinOrderRefunds.getType(), kylinOrderRefunds.getApplicantId(), kylinOrderRefunds.getApplicantName(),
kylinOrderRefunds.getApplicantAt(), kylinOrderRefunds.getReason(), kylinOrderRefunds.getCreatedAt()
kylinOrderRefunds.getApplicantAt(), kylinOrderRefunds.getReason(), null, null, kylinOrderRefunds.getCreatedAt()
});
sqlsDataD.add(new Object[]{
kylinOrderRefundEntities.getOrderRefundsEntitiesId(), kylinOrderRefundEntities.getOrderRefundsId(), kylinOrderRefundEntities.getRefundPrice(),
......
......@@ -16,7 +16,7 @@ kylin_order_ticket_entities.synPay=UPDATE kylin_order_ticket_entities SET is_pay
# ------------------------\u7528\u6237\u53D1\u8D77\u8BA2\u5355\u9000\u6B3E----------------------------
kylin_order_ticket_status.refund=UPDATE kylin_order_ticket_status SET status = ? ,updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
kylin_order_ticket_entities.refund=UPDATE kylin_order_ticket_entities SET is_payment = ? ,updated_at = ? WHERE order_ticket_entities_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
kylin_order_refund.refund=INSERT INTO kylin_order_refunds (order_refunds_id,order_tickets_id,order_refund_code,price,price_express,price_charges,ticket_num,status,type,applicant_id,applicant_name,applicant_at,reason,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)
kylin_order_refund.refund=INSERT INTO kylin_order_refunds (order_refunds_id,order_tickets_id,order_refund_code,price,price_express,price_charges,ticket_num,status,type,applicant_id,applicant_name,applicant_at,reason,reason_id,reason_detail,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
kylin_order_refund_entities.refund=INSERT INTO kylin_order_refund_entities (order_refunds_entities_id,order_refunds_id,refund_price,order_ticket_entities_id,created_at) VALUES(?,?,?,?,?)
kylin_order_refund_pic.refund=INSERT INTO kylin_order_refund_pic (refund_pic_id,order_refunds_id,pic_url,created_at) VALUES (?,?,?,?)
kylin_order_refund.refundOrderExpress=INSERT INTO kylin_order_express (order_express_id,performances_id,order_tickets_id,order_refunds_id,order_express_code,mailno,express_type,filter_result,remark,express_status,send_type,send_express_type,express_contacts,express_phone,send_express_address,appointment_time,pro_name,cargo_type_code,limit_type_code,express_type_code,goods_value_total,goods_number,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
......
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