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

Commit e9c4b455 authored by 姜秀龙's avatar 姜秀龙

会员需求1

parent 931332ff
......@@ -7,6 +7,7 @@ import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
@ApiModel(value = "AdamMemberBuildParam", description = "编辑会员入参")
@Data
......@@ -39,6 +40,8 @@ public class AdamMemberBuildParam implements Serializable {
@ApiModelProperty(required = true, value = "是否开售:1-开售|2-停售")
@NotNull(message = "是否开售不能为空")
private Integer onsale;
@ApiModelProperty(required = false, value = "双倍积分[1-关闭,2-开启]")
private BigDecimal integralRate;
@ApiModelProperty(required = false, value = "限购数量,0不限购[200]", example = "0")
private Integer limitation;
@ApiModelProperty(required = false, value = "限购开始时间[yyyy-MM-dd]")
......
......@@ -31,6 +31,10 @@ public class AdamMemberPriceBuildParam implements Serializable {
@ApiModelProperty(required = true, value = "有效天数")
@Min(value = 1, message = "有效天数不得小于1")
private Integer days;
@ApiModelProperty(required = false, value = "价格包名称")
private String name;
@ApiModelProperty(required = false, value = "价格状态[0-不可用|1-上线中|2-已下线],删除传2")
private Integer state;
@ApiModelProperty(required = false, value = "双倍积分[1-关闭,2-开启]")
private BigDecimal integralRate;
......
......@@ -36,4 +36,6 @@ public class AdamMemberRightsBuildParam implements Serializable {
private String detail;
@ApiModelProperty(required = false, value = "展示顺序")
private Integer seqNo;
@ApiModelProperty(required = false, value = "绑定价格包ID,空表示全档回退共用")
private String memberPriceId;
}
package com.liquidnet.service.adam.dto.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.entity.AdamAddresses;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.liquidnet.service.adam.entity.AdamMemberPrice;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
......@@ -10,18 +8,17 @@ import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@ApiModel(value = "AdamMemberPriceVo", description = "会员价格")
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class AdamMemberPriceVo implements Serializable {
private static final long serialVersionUID = -8106910166417645208L;
@ApiModelProperty(position = 1, value = "会员价格ID")
private String memberPriceId;
@ApiModelProperty(position = 2, value = "会员类型ID")
private String memberId;
// @ApiModelProperty(position = 3, value = "价格状态[0-不可用,1-上线中,2-已下线]")
// private Integer state;
@ApiModelProperty(position = 4, value = "价格包名称")
private String name;
@ApiModelProperty(position = 5, value = "原价")
......@@ -32,12 +29,8 @@ public class AdamMemberPriceVo implements Serializable {
private BigDecimal priceSpecial;
@ApiModelProperty(position = 8, value = "有效天数")
private Integer days;
// @ApiModelProperty(position = 8, value = "会员包须知")
// private String detail;
// @JsonFormat(shape=JsonFormat.Shape.STRING, pattern= DateUtil.DATE_FULL_STR)
// private LocalDateTime createdAt;
// @JsonFormat(shape=JsonFormat.Shape.STRING, pattern= DateUtil.DATE_FULL_STR)
// private LocalDateTime updatedAt;
@ApiModelProperty(position = 9, value = "本价格包权益")
private List<AdamMemberRightsVo> rightsVoList;
private static final AdamMemberPriceVo obj = new AdamMemberPriceVo();
......@@ -53,15 +46,11 @@ public class AdamMemberPriceVo implements Serializable {
if (null == source) return this;
this.setMemberPriceId(source.getMemberPriceId());
this.setMemberId(source.getMemberId());
// this.setState(source.getState());
this.setName(source.getName());
this.setPrice(source.getPrice());
this.setPriceFixed(source.getPriceFixed());
this.setPriceSpecial(source.getPriceSpecial());
this.setDays(source.getDays());
// this.setDetail(source.getDetail());
// this.setCreatedAt(source.getCreatedAt());
// this.setUpdatedAt(source.getUpdatedAt());
return this;
}
}
......@@ -25,6 +25,8 @@ public class AdamMemberRightsVo implements java.io.Serializable, Cloneable {
private String cover;
@ApiModelProperty(position = 7, value = "详情内容")
private String detail;
@ApiModelProperty(position = 8, value = "绑定价格包ID,空表示全档回退共用")
private String memberPriceId;
public AdamMemberRightsVo setSeqNo(Integer seqNo) {
this.seqNo = seqNo;
......@@ -60,6 +62,7 @@ public class AdamMemberRightsVo implements java.io.Serializable, Cloneable {
this.setLabel(source.getLabel());
this.setCover(source.getCover());
this.setDetail(source.getDetail());
this.setMemberPriceId(source.getMemberPriceId());
return this;
}
}
......@@ -62,10 +62,8 @@ public class AdamMemberVo implements java.io.Serializable, Cloneable {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateUtil.DATE_FULL_STR)
private LocalDateTime limiteAt;
@ApiModelProperty(position = 20, value = "会员卡价格信息")
@ApiModelProperty(position = 20, value = "会员卡价格信息(含各档 rightsVoList)")
private List<AdamMemberPriceVo> priceVoList;
@ApiModelProperty(position = 21, value = "会员专属权益")
private List<AdamMemberRightsVo> rightsVoList;
@ApiModelProperty(position = 22, value = "用户会员信息")
private AdamUserMemberVo userMemberVo;
@ApiModelProperty(position = 23, value = "购买会员限购[0-名额已满|1-允许购买|2-限购未开始]")
......
package com.liquidnet.service.adam.dto.vo.admin;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 会员订单详情-优先券使用情况
*/
@Data
public class AdamMemberPriorityCouponAdminVo implements Serializable {
private static final long serialVersionUID = 1L;
private String ucouponId;
private String name;
private String mobile;
/** 专享标识文案 */
private String exclusiveLabel;
private Integer exclusive;
/** 用户券状态文案 */
private String stateLabel;
private Integer state;
private String title;
/** 券类型文案 */
private String couTypeLabel;
private Integer couType;
private LocalDateTime bindAt;
private LocalDateTime duedAt;
private LocalDateTime usedAt;
}
......@@ -7,6 +7,8 @@ import com.liquidnet.service.adam.dto.vo.AdamUserMemberVo;
import com.liquidnet.service.adam.entity.AdamUser;
import lombok.Data;
import java.util.List;
/**
* @author AnJiabin <anjiabin@zhengzai.tv>
* @version V1.0
......@@ -23,4 +25,6 @@ public class MemberOrderDetailAdminVo {
private AdamUserInfoVo adamUserInfoVo;
private AdamUserMemberVo adamUserMemberVo;
private AdamMemberOrderExtVo adamMemberOrderExtVo;
/** 该用户优先券(cou_type=101)使用情况 */
private List<AdamMemberPriorityCouponAdminVo> priorityCouponList;
}
......@@ -35,6 +35,8 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
......@@ -102,6 +104,10 @@ public class AdamMemberAdminController extends BaseController {
)
);
mmap.put("member", member);
mmap.put("memberPriceList", java.util.Collections.emptyList());
mmap.put("memberRightsList", java.util.Collections.emptyList());
mmap.put("couponMemberDtoListMap", java.util.Collections.emptyMap());
mmap.put("memberPrice", null);
if (null != member) {
List<AdamMemberRights> memberRightsList = adamMemberRightsAdminService.list(Wrappers.lambdaQuery(AdamMemberRights.class)
......@@ -109,6 +115,8 @@ public class AdamMemberAdminController extends BaseController {
.eq(AdamMemberRights::getMemberId, member.getMemberId())
.select(
AdamMemberRights::getMrightsId,
AdamMemberRights::getMemberId,
AdamMemberRights::getMemberPriceId,
AdamMemberRights::getCover,
AdamMemberRights::getTitle,
AdamMemberRights::getSubTitle,
......@@ -116,6 +124,7 @@ public class AdamMemberAdminController extends BaseController {
)
);
mmap.put("memberRightsList", memberRightsList);
mmap.put("couponMemberDtoListMap", java.util.Collections.emptyMap());
if (!CollectionUtils.isEmpty(memberRightsList)) {
List<String> mrightsIdList = memberRightsList.stream().map(AdamMemberRights::getMrightsId).collect(Collectors.toList());
......@@ -127,17 +136,28 @@ public class AdamMemberAdminController extends BaseController {
mmap.put("couponMemberDtoListMap", couponMemberDtoListMap);
}
AdamMemberPrice memberPrice = adamMemberPriceAdminService.getOne(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getState, 1)
List<AdamMemberPrice> memberPriceList = adamMemberPriceAdminService.list(Wrappers.lambdaQuery(AdamMemberPrice.class)
.in(AdamMemberPrice::getState, Arrays.asList(1, 2))
.eq(AdamMemberPrice::getMemberId, member.getMemberId())
.orderByAsc(AdamMemberPrice::getState)
.orderByAsc(AdamMemberPrice::getDays)
.select(
AdamMemberPrice::getMemberPriceId,
AdamMemberPrice::getMemberId,
AdamMemberPrice::getName,
AdamMemberPrice::getPrice,
AdamMemberPrice::getPriceFixed,
AdamMemberPrice::getPriceSpecial,
AdamMemberPrice::getDays
AdamMemberPrice::getDays,
AdamMemberPrice::getState
)
);
mmap.put("memberPriceList", memberPriceList);
AdamMemberPrice memberPrice = CollectionUtils.isEmpty(memberPriceList) ? null
: memberPriceList.stream()
.filter(p -> p.getState() != null && p.getState() == 1)
.max(Comparator.comparing(AdamMemberPrice::getDays, Comparator.nullsFirst(Integer::compareTo)))
.orElse(null);
mmap.put("memberPrice", memberPrice);
}
mmap.put("platformUrl", platformUrl);
......@@ -167,7 +187,7 @@ public class AdamMemberAdminController extends BaseController {
@PostMapping("edit_price")
@ResponseBody
public AjaxResult editPrice(@Validated AdamMemberPriceBuildParam parameter) {
return this.toAjax(adamMemberPriceAdminService.edit(parameter));
return AjaxResult.success(adamMemberPriceAdminService.edit(parameter));
}
@ApiOperation(value = "会员套餐:权益编辑")
......@@ -179,13 +199,27 @@ public class AdamMemberAdminController extends BaseController {
if (parameter.getState() != 2) {
// 这里编辑包含新增、更新、删除分别对应state:1,1,2
parameter.setState(1);
if (StringUtils.isBlank(parameter.getMemberPriceId())) {
return AjaxResult.warn("请指定权益所属价格包");
}
} else if (StringUtils.isBlank(parameter.getMrightsId())) {
return AjaxResult.warn("请指定要删除的权益ID");
}
if (null == parameter.getSeqNo()) {
parameter.setSeqNo(0);
}
return this.toAjax(adamMemberRightsAdminService.edit(parameter));
AdamMemberRights rights = adamMemberRightsAdminService.edit(parameter);
if (rights == null || StringUtils.isBlank(rights.getMrightsId())) {
return AjaxResult.error("权益保存失败");
}
// 显式回传 id,避免实体序列化字段名差异导致前端读不到
Map<String, Object> data = new HashMap<>(8);
data.put("mrightsId", rights.getMrightsId());
data.put("memberId", rights.getMemberId());
data.put("memberPriceId", rights.getMemberPriceId());
data.put("state", rights.getState());
data.put("title", rights.getTitle());
return AjaxResult.success(data);
}
// @ApiOperation(value = "会员套餐:券编辑")
......
......@@ -25,10 +25,12 @@ 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.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Api(tags = "会员码管理")
......@@ -46,34 +48,69 @@ public class AdamMemberCodeAdminController extends BaseController {
@GetMapping("mgt")
public String viewCodeMgt(@RequestParam String memberId, ModelMap mmap) {
mmap.put("memberId", memberId);
AdamMemberPrice memberPrice = adamMemberPriceAdminService.getOne(Wrappers.lambdaQuery(AdamMemberPrice.class)
List<AdamMemberPrice> memberPriceList = adamMemberPriceAdminService.list(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getState, 1)
.eq(AdamMemberPrice::getMemberId, memberId)
.select(
AdamMemberPrice::getMid,
AdamMemberPrice::getMemberPriceId,
AdamMemberPrice::getName,
AdamMemberPrice::getPrice,
AdamMemberPrice::getPriceFixed,
AdamMemberPrice::getPriceSpecial,
AdamMemberPrice::getDays
)
.orderByAsc(AdamMemberPrice::getDays)
);
// 默认选中天数最长的价格包(通常为年卡),创建时可改选其它档
AdamMemberPrice memberPrice = CollectionUtils.isEmpty(memberPriceList) ? null
: memberPriceList.stream()
.max(Comparator.comparing(AdamMemberPrice::getDays, Comparator.nullsFirst(Integer::compareTo)))
.orElse(null);
mmap.put("memberPrice", memberPrice);
mmap.put("memberPriceList", memberPriceList);
return prefix + "/code_mgt";
}
@ApiOperation(value = "会员码:上线价格包列表")
@RequiresPermissions("adam:member:code:creation")
@GetMapping("mgt/prices")
@ResponseBody
public AjaxResult listOnlinePrices(@RequestParam String memberId) {
List<AdamMemberPrice> memberPriceList = adamMemberPriceAdminService.list(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getState, 1)
.eq(AdamMemberPrice::getMemberId, memberId)
.select(
AdamMemberPrice::getMid,
AdamMemberPrice::getMemberPriceId,
AdamMemberPrice::getName,
AdamMemberPrice::getPrice,
AdamMemberPrice::getPriceFixed,
AdamMemberPrice::getPriceSpecial,
AdamMemberPrice::getDays
)
.orderByAsc(AdamMemberPrice::getDays)
);
return AjaxResult.success(memberPriceList);
}
@RequiresPermissions("adam:member:code:mgt:list")
@Log(title = "会员管理:会员码:管理", businessType = BusinessType.LIST)
@PostMapping("mgt/list")
@ResponseBody
public TableDataInfo listCodeMgt(@RequestParam String memberId) {
// TODO: 2021/9/23 码生成记录
LambdaQueryWrapper<AdamMemberCodeBatch> queryWrapper = Wrappers.lambdaQuery(AdamMemberCodeBatch.class);
queryWrapper.eq(AdamMemberCodeBatch::getState, 1);
// 本页按会员进入:只看该会员批次(member_id 精确匹配)
if (StringUtils.isNotBlank(memberId)) {
queryWrapper.eq(AdamMemberCodeBatch::getMemberId, memberId);
}
queryWrapper.select(
AdamMemberCodeBatch::getBatchNo,
AdamMemberCodeBatch::getMemberName,
AdamMemberCodeBatch::getMemberCombo,
AdamMemberCodeBatch::getMemberPriceId,
AdamMemberCodeBatch::getGenNum,
AdamMemberCodeBatch::getUseNum,
AdamMemberCodeBatch::getDetail,
......
......@@ -2,13 +2,12 @@
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('会员码管理')" />
<th:block th:include="include :: select2-css" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" href="#member-gencode-form" data-toggle="modal" shiro:hasPermission="adam:member:code:creation">
<a id="btnCreateMemberCode" class="btn btn-success" href="javascript:void(0)" onclick="openCreateModal()" shiro:hasPermission="adam:member:code:creation">
<i class="fa fa-plus"></i> 创建
</a>
</div>
......@@ -24,14 +23,21 @@
<form role="form" id="form-member-code-generate">
<input name="memberId" type="hidden" th:value="${memberId}" readonly>
<input name="memberPriceId" type="hidden" th:value="${memberPrice.memberPriceId}" readonly>
<div class="form-group">
<label class="pull-left">生产数量:</label>
<input type="number" name="genNum" class="form-control" style="width: 30%;height: 20px;" placeholder="生产数量">
<label>兑换套餐:</label>
<!-- 不用 select2:动态 option 会被 select2 吃掉只剩一条 -->
<select name="memberPriceId" id="memberPriceId" class="form-control noselect2" required>
<option value="">加载中...</option>
</select>
<p class="help-block m-b-none text-muted" style="margin-top:6px;">兑换后按所选价格包天数开会员,并发放该档权益</p>
</div>
<div class="form-group">
<!-- <label>备注:</label>-->
<textarea rows="3" name="detail" placeholder="备注描述" class="form-control"></textarea>
<label>生产数量:</label>
<input type="text" name="genNum" class="form-control" placeholder="请输入生产数量(1-100)" maxlength="3">
</div>
<div class="form-group">
<label>备注描述:</label>
<textarea rows="3" name="detail" placeholder="请输入备注描述" class="form-control"></textarea>
</div>
<div class="text-center" style="margin-bottom: -15px;margin-top: 10px;">
<button class="btn btn-sm btn-primary m-t-n-xs" type="button" onclick="memberCodeGenerate()">
......@@ -45,33 +51,48 @@
</div>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: select2-js" />
<script th:inline="javascript">
var viewMemberCodeListFlg = [[${@permission.hasPermi('adam:member:code:list')}]];
var memberId = [[${memberId}]];
var prefix = ctx + "adam/mcode";
var onlinePriceList = [];
$(function() {
var options = {
url: prefix + "/mgt/list?memberId=" + memberId,
url: prefix + "/mgt/list",
createUrl: prefix + "/mgt/creation/{id}",
exportUrl: prefix + "/export_code",
detailUrl: ctx + "adam/order/orderList/detail/{id}",
sortName: "createdAt",
sortOrder: "desc",
modalName: "会员码创建记录",
queryParams: function (params) {
return {
pageSize: params.limit,
pageNum: params.offset / params.limit + 1,
searchValue: params.search,
orderByColumn: params.sort,
isAsc: params.order,
memberId: memberId
};
},
columns: [{
checkbox: true
},
{
field: 'memberName',
title: '会员名称',
width: 220
width: 180
},
{
field: 'memberCombo',
title: '会员套餐名称',
width: 180
title: '兑换套餐',
width: 200
},
{
field: 'memberPriceId',
title: '价格包ID',
width: 140
},
{
field: 'genNum',
......@@ -86,7 +107,7 @@
{
field: 'detail',
title: '备注',
width: 280
width: 220
},
{
field: 'operator',
......@@ -112,9 +133,10 @@
}]
};
$.table.init(options);
// 打开列表页就拉套餐
reloadOnlinePrices();
});
/* 会员码列表 */
function memberCodeList(batchNo) {
$.modal.openTab("会员码列表", prefix + '/mgt/code?batchNo=' + batchNo);
}
......@@ -122,6 +144,9 @@
$("#form-member-code-generate").validate({
onkeyup: false,
rules:{
memberPriceId: {
required: true
},
genNum: {
required: true,
number: true,
......@@ -135,6 +160,9 @@
}
},
messages: {
memberPriceId: {
required: "请选择兑换套餐"
},
genNum: {
required: "请输入生产数量",
number: "生产数量必须为数字",
......@@ -146,28 +174,85 @@
minlength: "备注描述不得少于2个字符",
maxlength: "备注描述不得大于100个字符"
}
},
focusCleanup: true
});
function fillPriceOptions(list) {
onlinePriceList = list || [];
var $sel = $('#memberPriceId');
$sel.empty();
if (!onlinePriceList.length) {
$sel.append($('<option></option>').val('').text('暂无上线价格包'));
$('#btnCreateMemberCode').addClass('disabled');
return;
}
// 默认不选中:合上时不会误以为只有年卡一条;点开可见全部上线档
$sel.append($('<option></option>').val('').text('请选择兑换套餐(共' + onlinePriceList.length + '档)'));
$.each(onlinePriceList, function (i, mp) {
var text = (mp.name || '未命名') + ' · ' + (mp.days != null ? mp.days : '-') + '天 · ¥' + (mp.price != null ? mp.price : '-');
$sel.append($('<option></option>').attr('value', mp.memberPriceId).text(text));
});
$('#btnCreateMemberCode').removeClass('disabled');
console.log('[member-code] online prices:', onlinePriceList.length, onlinePriceList);
}
function reloadOnlinePrices(done) {
$.ajax({
url: prefix + '/mgt/prices',
type: 'get',
data: { memberId: memberId },
dataType: 'json',
success: function (res) {
var list = (res && res.code == web_status.SUCCESS && res.data) ? res.data : [];
fillPriceOptions(list);
if (typeof done === 'function') {
done(list);
}
},
error: function () {
fillPriceOptions([]);
$.modal.msgWarning("加载兑换套餐失败,请刷新页面重试");
if (typeof done === 'function') {
done([]);
}
}
});
}
function openCreateModal() {
if ($('#btnCreateMemberCode').hasClass('disabled')) {
$.modal.alertWarning("暂无上线价格包,请先在会员套餐中配置并上线");
return;
}
reloadOnlinePrices(function (list) {
if (!list.length) {
$.modal.alertWarning("暂无上线价格包,请先在会员套餐中配置并上线");
return;
}
$('#member-gencode-form').modal('show');
});
}
function memberCodeGenerate() {
if (!$('#memberPriceId').val()) {
$.modal.alertWarning("请选择兑换套餐");
return;
}
if ($.validate.form("form-member-code-generate")) {
$('#member-gencode-form').hide();
$('#member-gencode-form').modal('hide');
$.operate.saveModal(prefix + "/mgt/creation", $('#form-member-code-generate').serialize(), memberCodeGenerateCallback);
}
}
function memberCodeGenerateCallback(result) {
if (typeof callback == "function") {
callback(result);
}
if (result.code == web_status.SUCCESS) {
$.modal.alertSuccess(result.msg);
setTimeout("refreshTab()", 1500);
} else if (result.code == web_status.WARNING) {
$('#member-gencode-form').show();
$('#member-gencode-form').modal('show');
$.modal.alertWarning(result.msg);
} else {
$('#member-gencode-form').show();
$('#member-gencode-form').modal('show');
$.modal.alertError(result.msg);
}
}
......
......@@ -23,6 +23,13 @@
.interests_column:after {
clear: both;
}
.modal-body { max-height: 70vh; overflow-y: auto; }
#modalRightsArea .interests_item {
width: 100%;
height: auto;
float: none;
margin-left: 0 !important;
}
.interests_item {
width: 500px;
height: 950px;
......@@ -54,10 +61,17 @@
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
display: flex;
/* 价格包 Bootstrap 弹窗已有遮罩,这里不再铺厚黑底,避免「黑影」叠两层 */
background: rgba(0, 0, 0, .15);
/* 默认隐藏,打开时再加 .is-open;避免 display:flex 与 hide() 打架留下黑影 */
display: none;
justify-content: center;
align-items: center;
/* 全局 .modal 为 2050 !important,必须更高才能盖住价格包弹窗 */
z-index: 10000 !important;
}
.coupe_pop.is-open, .cover_pop.is-open {
display: flex !important;
}
.pop_inner {
width: 500px;
......@@ -145,6 +159,16 @@
.interestsDesc_inner {
height: 300px;
}
.coupe_pop .pop_inner {
overflow-y: auto;
height: auto;
max-height: 90vh;
position: relative;
z-index: 10001;
}
.coupe_pop .form-control {
pointer-events: auto;
}
</style>
<body class="gray-bg" style="font: 14px Helvetica Neue, Helvetica, PingFang SC, 微软雅黑, Tahoma, Arial, sans-serif !important;">
<section class="section-content">
......@@ -234,105 +258,85 @@
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-sm btn-primary" onclick="submitMemberInfo()"><i class="fa fa-check"></i>保 存</button>&nbsp;
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭 </button>
</div>
</div>
</form>
</div>
<!--会员套餐-->
<div class="tab-pane" id="membership">
<form class="form-horizontal" id="form-membership-edit">
<div class="form-group price_item">
<label class="col-sm-2 control-label">会员价格:</label>
<div class="col-sm-10">
<label>原价(元)</label>
<input type="text" class="form-control" name="price" placeholder="请输入原价" th:value="${memberPrice.price}"/>
</div>
<div class="col-sm-10">
<label>购买价格(元)</label>
<input type="text" class="form-control" name="priceFixed" placeholder="请输入购买价格" th:value="${memberPrice.priceFixed}"/>
</div>
<div class="col-sm-10">
<label>首年优惠(元)</label>
<input type="text" class="form-control" name="priceSpecial" placeholder="请输入优惠价格" th:value="${memberPrice.priceSpecial}"/>
</div>
<div class="col-sm-10">
<label>有效时间(天)</label>
<input type="text" class="form-control" name="days" placeholder="请输入有效时间" th:value="${memberPrice.days}"/>
</div>
</div>
<div class="form-group" disabled="none">
<label class="col-sm-2 control-label">双倍积分:</label>
<div class="col-sm-10">
<div class="radio-box">
<input type="radio" id="integral1" name="integralRate" value="1" th:attr="checked=${member.integralRate == 1}"/>
<label for="radio1"></label>
<input type="radio" id="integral1" name="integralRate" value="1" th:attr="checked=${member.integralRate == null or member.integralRate.intValue() == 1}"/>
<label for="integral1"></label>
</div>
<div class="radio-box">
<input type="radio" id="integral2" name="integralRate" value="2" th:attr="checked=${member.integralRate == 2}"/>
<label for="radio2"></label>
<input type="radio" id="integral2" name="integralRate" value="2" th:attr="checked=${member.integralRate != null and member.integralRate.intValue() == 2}"/>
<label for="integral2"></label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="btn btn-sm btn-primary" onclick="submitMemberShip()"><i class="fa fa-check"></i>保 存</button>&nbsp;
<button type="button" class="btn btn-sm btn-primary" onclick="submitMemberInfo()"><i class="fa fa-check"></i>保 存</button>&nbsp;
<button type="button" class="btn btn-sm btn-danger" onclick="closeItem()"><i class="fa fa-reply-all"></i>关 闭 </button>
</div>
</div>
</form>
<form class="form-horizontal" id="form-membercoupe-edit">
<div class="interests_column">
<div class="form-group">
<label class="col-sm-2 control-label">权益描述:</label>
</div>
<div th:if="${#lists.isEmpty(memberRightsList)}">
<div class="form-group interests_item">
<div class="text-center">
<p class="user-info-head avatar_img vip_icon" onclick="icon(this)">
<img class="img-circle img-lg interestsIcon" id="interestsIcon"/>
</p>
<input type="text" class="form-control" id="interestsTitle" name="interestsTitle" placeholder="请输入标题"/>
<input type="text" class="form-control" id="interestsSubTitle" name="interestsSubTitle" placeholder="请输入副标题"/>
<textarea class="form-control interestsDesc_inner" id="interestsDesc" name="interestsDesc" placeholder="详情内容"></textarea>
<div class="member-coupes-item">
<ul>
<div class="member-coupes-inner">
<div class="member-coupes-detail">
<input type="hidden"/>
<span class="member-coupes-mcouponId" hidden></span>
<span class="member-coupes-busiType"></span>
<span class="member-coupes-type"></span>
<span class="member-coupes-title"></span>
<span class="member-coupes-mark"></span>
<span class="member-coupes-num"></span>
<span class="member-coupes-time"></span>
</div>
<div class="member-coupes-bth">
<!-- <span onclick="">编辑</span>-->
<span onclick="editCoupes(this)">编辑</span>
<span class="delCoupes" onclick="delCoupes(this)">删除</span>
</div>
</div>
</ul>
</div>
<div class="member-coupes" onclick="addCoupes(this)">+添加券<br/>*全场券不需要重复添加,需要先保存再添加券</div>
<div class="go-member-item" onclick="addColumn(this)">继续添加</div>
<div class="go-member-item save_mermber" onclick="saveColumn(this, 1)">保存</div>
<!--会员套餐-->
<div class="tab-pane" id="membership">
<div class="form-horizontal">
<div class="form-group">
<div class="col-sm-12" style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
<label class="control-label" style="margin:0;">价格包列表</label>
<button type="button" class="btn btn-sm btn-success" onclick="openPriceModal('add')"><i class="fa fa-plus"></i> 新增价格包</button>
</div>
<div class="col-sm-12">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>价格包ID</th>
<th>名称</th>
<th>原价</th>
<th>购买价</th>
<th>首购特价</th>
<th>天数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="mp : ${memberPriceList}">
<td th:text="${mp.memberPriceId}"></td>
<td th:text="${mp.name}"></td>
<td th:text="${mp.price}"></td>
<td th:text="${mp.priceFixed}"></td>
<td th:text="${mp.priceSpecial}"></td>
<td th:text="${mp.days}"></td>
<td>
<span class="badge badge-primary" th:if="${mp.state == 1}">上线中</span>
<span class="badge" th:if="${mp.state == 2}">已下线</span>
</td>
<td>
<a class="btn btn-xs btn-info" href="javascript:void(0)"
th:onclick="openPriceModal('edit', findPriceById([[${mp.memberPriceId}]]))">编辑</a>
<a class="btn btn-xs btn-danger" href="javascript:void(0)" th:if="${mp.state == 1}"
th:onclick="offlinePrice([[${mp.memberPriceId}]])">下线</a>
<a class="btn btn-xs btn-warning" href="javascript:void(0)" th:if="${mp.state == 2}"
th:onclick="onlinePrice([[${mp.memberPriceId}]])">重新上线</a>
</td>
</tr>
<tr th:if="${memberPriceList == null or #lists.isEmpty(memberPriceList)}">
<td colspan="8" class="text-center">暂无价格包,请点击右上角新增</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div th:each="mr:${memberRightsList}" class="interests_inner">
<div class="form-group interests_item">
<!-- 预渲染权益卡片仓库(按价格包过滤克隆进弹窗) -->
<div id="rights-store" style="display:none;">
<div th:each="mr:${memberRightsList}" class="rights-store-item interests_inner"
th:attr="data-price-id=${mr.memberPriceId}, data-mrights-id=${mr.mrightsId}">
<div class="form-group interests_item" th:attr="data-mrights-id=${mr.mrightsId}">
<input type="hidden" class="js-mrights-id" th:value="${mr.mrightsId}"/>
<div class="text-center">
<p class="user-info-head avatar_img vip_icon" onclick="icon(this)">
<img class="img-circle img-lg interestsIcon" id="interestsIcon" th:src="${mr.cover}"/>
......@@ -350,7 +354,7 @@
<input type="hidden" id="mrccouType" th:value="${mrc.couType}"/>
<input type="hidden" id="mrctitle" th:value="${mrc.title}"/>
<input type="hidden" id="mrceventAmt" th:value="${mrc.eventAmt}"/>
<input type="hidden" id="mrcdays" th:value="${memberPrice.days}"/>
<input type="hidden" id="mrcdays" class="mrcdays" value=""/>
<input type="hidden" id="mrvalFace" th:value="${mrc.valFace}"/>
<input type="hidden" id="mrcoverlay" th:value="${mrc.overlay}"/>
<input type="hidden" id="mrcvalMinus" th:value="${mrc.valMinus}"/>
......@@ -360,21 +364,15 @@
<span class="member-coupes-busiType" th:if="${mrc.busiType == 1}">演出</span>
<span class="member-coupes-busiType" th:if="${mrc.busiType == 2}">商品</span>
<span class="member-coupes-busiType" th:if="${mrc.busiType == 3}">优先购</span>
<span class="member-coupes-type" th:if="${mrc.couType == 1}">代金券</span>
<span class="member-coupes-type" th:if="${mrc.couType == 2}">满减券</span>
<span class="member-coupes-type" th:if="${mrc.couType == 101}">优先券</span>
<span class="member-coupes-title" th:text="${mrc.title}"></span>
<span class="member-coupes-mark">通用</span>
<span class="member-coupes-num" th:text="${mrc.eventAmt} + '张'"></span>
<span class="member-coupes-time" th:text="${memberPrice.days} + '天'"></span>
<span class="member-coupes-time package-days-text"></span>
</div>
<div class="member-coupes-bth">
<!-- <span onclick="">编辑</span>-->
<span th:onclick="editCoupes(this, [[${mrc}]]);">编辑</span>
<span class="delCoupes" onclick="delCoupes(this)">删除</span>
</div>
......@@ -389,11 +387,27 @@
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div id="rights-empty-template" style="display:none;">
<div class="interests_inner">
<div class="form-group interests_item" data-mrights-id="">
<input type="hidden" class="js-mrights-id" value=""/>
<div class="text-center">
<p class="user-info-head avatar_img vip_icon" onclick="icon(this)">
<img class="img-circle img-lg interestsIcon" id="interestsIcon"/>
</p>
<input type="text" class="form-control" id="interestsTitle" name="interestsTitle" placeholder="请输入标题"/>
<input type="text" class="form-control" id="interestsSubTitle" name="interestsSubTitle" placeholder="请输入副标题"/>
<textarea class="form-control interestsDesc_inner" id="interestsDesc" name="interestsDesc" placeholder="详情内容"></textarea>
<div class="member-coupes-item"></div>
<div class="member-coupes" onclick="addCoupes(this)">+添加券<br/>*全场券不需要重复添,需要先保存再添加券</div>
<div class="go-member-item" onclick="addColumn(this)">继续添加</div>
<div class="go-member-item save_mermber" onclick="saveColumn(this, 1)">保存</div>
<div class="go-member-item del_mermber" onclick="saveColumn(this, 2)">删除</div>
</div>
</div>
</div>
</div>
</form>
</div>
<!--会员码-->
......@@ -409,6 +423,68 @@
</div>
</div>
<!-- 价格包编辑弹窗 -->
<div class="modal fade" id="pricePackageModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" style="width:720px;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
<h4 class="modal-title" id="pricePackageModalTitle">编辑价格包</h4>
</div>
<div class="modal-body">
<form class="form-horizontal" id="form-membership-edit">
<input type="hidden" name="memberPriceId" id="editMemberPriceId" value=""/>
<h5 style="margin-top:0;">价格信息</h5>
<div class="form-group">
<label class="col-sm-3 control-label">名称</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="name" id="editPriceName" placeholder="如:年卡/月卡/体验3天"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">原价(元)</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="price" id="editPrice" placeholder="请输入原价"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">购买价格(元)</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="priceFixed" id="editPriceFixed" placeholder="请输入购买价格"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">首购特价(元)</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="priceSpecial" id="editPriceSpecial" placeholder="请输入优惠价格"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">有效时间(天)</label>
<div class="col-sm-8">
<input type="text" class="form-control" name="days" id="editDays" placeholder="请输入有效时间"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-8">
<button type="button" class="btn btn-sm btn-primary" onclick="submitMemberShip()">保存价格包</button>
</div>
</div>
</form>
<hr/>
<h5>本档权益 <small id="modalRightsHint" style="color:#999;"></small></h5>
<div id="modalRightsLockTip" class="text-muted" style="display:none;margin-bottom:10px;">请先保存价格包,再配置本档权益</div>
<form class="form-horizontal" id="form-membercoupe-edit">
<div class="interests_column" id="modalRightsArea"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="cover_pop" style="display: none">
<div class="pop_inner">
<input type="hidden" class="coverFunNum"/>
......@@ -463,7 +539,7 @@
</div>
</div>
</div>
<div class="form-group alltrack">
<div class="form-group alltrack alltrack_type_row">
<label class="col-sm-2 control-label">类型:</label>
<div class="col-sm-10 alltrack_radio">
<div class="radio-box">
......@@ -476,25 +552,26 @@
<input type="radio" id="coupetypr1" class="coupeAddVal" name="coupe_type" value="1"/>代金券
</label>
</div>
<!-- 会员套餐页暂不支持折扣券:后端 edit_coupon 商品类别仅允许 couType 1/2/3,选 4 会报「暂不支持所选券类型」;保留注释便于后续启用
<div class="radio-box goods_track">
<label for="radio2" onclick="coupetType(this)">
<input type="radio" id="coupetypr4" class="coupeAddVal" name="coupe_type" value="4"/>折扣券
</label>
</div>
-->
</div>
<!-- <div class="col-sm-10 discount">-->
<!-- <div class="coupetypr_discount">满 ¥ <input type="text" id="valOver"/> 减 ¥ <input type="text" id="valMinus"/></div>-->
<!-- <div class="coupetypr_price" style="display: none;">金额 ¥ <input type="text" id="valFace"/></div>-->
<!-- </div>-->
</div>
<div class="form-group coupe-select-row">
<label class="col-sm-2 control-label">选择券:</label>
<div class="col-sm-10 discount">
<div class="coupetypr_search">
<div class="remote_wrapper" style="position: relative;">
<input name="ids" type="hidden">
<input type="text" id="remoteSearch" placeholder="请选择券">
<input type="text" id="remoteSearch" class="form-control" placeholder="请输入关键字选择券" autocomplete="off">
</div>
</div>
</div>
<div class="form-group user_rules">
<div class="form-group user_rules" style="width:100%;">
<label class="col-sm-2 control-label">适用范围:</label>
<div class="col-sm-10 rules_inner"></div>
</div>
......@@ -502,7 +579,7 @@
<div class="form-group">
<label class="col-sm-2 control-label">数量:</label>
<div class="col-sm-10">
<input type="text" id="eventAmt" value="1"/>
<input type="text" id="eventAmt" class="form-control" value="1" style="display:inline-block;width:120px;" autocomplete="off"/>
</div>
</div>
<!-- <div class="form-group alltrack_limit">-->
......@@ -539,11 +616,13 @@
var memberRightsList = [[${memberRightsList}]];
var couponMemberDtoListMap = [[${couponMemberDtoListMap}]];
var memberPrice = [[${memberPrice}]];
var memberPriceList = [[${memberPriceList}]];
var typeRadio = '';
var prefix = ctx + "adam/member";
var candyfix = ctx + "candy/coupon";
window.candyCouponApi = candyfix;
console.log(memberRightsList);
// 弹窗
......@@ -569,11 +648,13 @@
});
var tabActive = window.localStorage.getItem('tabActive');
if (tabActive == 1) {
$('.tabs_inner').removeClass('active');
$('.tab-pane').removeClass('active');
$('.tabs_inner').eq(Number(tabActive)).addClass('active');
$('.tab-pane').eq(Number(tabActive)).addClass('active');
if (tabActive == '1' || tabActive == 1) {
// 刷新后强制回到套餐 Tab(兼容 bootstrap tab 偶发不激活)
$('.nav-tabs li').removeClass('active');
$('.tab-content > .tab-pane').removeClass('active in');
$('.nav-tabs a[href="#membership"]').parent().addClass('active');
$('#membership').addClass('active in');
$('.nav-tabs a[href="#membership"]').tab('show');
}
$('.coupenColumn .iCheck-helper').click(function () {
......@@ -606,8 +687,7 @@
// 弹窗确认取消按钮
function popBtn (num) {
if (num == 0) {
$('.cover_pop').hide();
$('.coupe_pop').hide();
clearUiShade();
} else if (num == 2) {
if ($('.coverFunNum').val() == 1) {
var bannerCon = $('.coverCon').val();
......@@ -618,23 +698,41 @@
} else {
var coverIndex = $('.coverIndex').val();
var coverCon = $('.coverCon').val();
$('.vip_icon').find('img').eq(coverIndex).attr('src', coverCon);
var $icons = $('#modalRightsArea .vip_icon').find('img');
if (!$icons.length) {
$icons = $('.vip_icon').find('img');
}
$icons.eq(coverIndex).attr('src', coverCon);
}
$('.cover_pop').hide();
hideCoverPop();
} else {
var coupeTitle = $('#coupeAdd_title').val();
var busiType = this.typeRadio;
if (this.typeRadio == 0) {
var coupeTitle = $('#coupeAdd_title').val() || $('#remoteSearch').val() || '';
// 以勾选/已选券元数据为准,避免残留「优先购+101」污染其它券回显
var typeMeta = resolveCoupeTypeMeta();
var busiType = typeMeta.busiType;
var couType = typeMeta.couType;
if (busiType === '' || busiType == null || isNaN(Number(busiType))) {
$.modal.msgWarning('请先选择券类别');
return;
}
if (Number(busiType) !== 3 && (couType === '' || couType == null || isNaN(Number(couType)))) {
$.modal.msgWarning('请先选择券类型');
return;
}
busiType = Number(busiType);
couType = Number(busiType) === 3 ? 101 : Number(couType);
if (busiType == 0) {
var couponRuleList = [{
useScope: 100,
busiName: '全场'
}]
} else if ( this.typeRadio == 1) {
} else if (busiType == 1) {
var couponRuleList = [{
useScope: 90,
busiName: '演出'
}]
} else if ( this.typeRadio == 2) {
} else if (busiType == 2) {
var couponRuleList = [{
useScope: 80,
busiName: '商品'
......@@ -646,11 +744,10 @@
}]
}
if (this.typeRadio == 3) {
if (busiType == 3) {
var eventAmt = $('#eventAmt').val();
var overlay = this.overlay;
} else {
var couType = this.typeCoupe;
var valOver = $('#valOver').val();
var valMinus = $('#valMinus').val();
if (couType == 1) {
......@@ -659,17 +756,17 @@
}
var itemIndex = $('.parentsIndex').val();
var mrightsId = this.mrightsId;
var couponId = this.couponId;
var couponId = this.couponId || window.couponId;
var MembercoupenData = {
busiType: Number(busiType),
couType: Number(busiType) == 3 ? 101 : Number(couType),
busiType: busiType,
couType: couType,
couponId: couponId? couponId : '',
couponRuleList: couponRuleList,
eventAmt: $('#eventAmt').val(),
label: "标注",
memberId: member.memberId,
mrightsId: mrightsId ? mrightsId : memberRightsList[itemIndex].mrightsId ? memberRightsList[itemIndex].mrightsId : 'string',
mrightsId: mrightsId ? mrightsId : 'string',
// overlay: overlay ? overlay : 0,
overlay: 1,
title: coupeTitle,
......@@ -681,11 +778,17 @@
if ($('.pop_oper').val() == 3) {
MembercoupenData.operCouponId = this.operCouponId;
}
$.operate.saveModalBody(prefix + "/edit_coupon", JSON.stringify(MembercoupenData), function () {
$('.coupe_pop').hide();
$('.cover_pop').hide();
window.location.reload();
});
packageSave(prefix + "/edit_coupon", JSON.stringify(MembercoupenData), function (result) {
// 无论成败先关弹层,避免半透明黑影残留
hideCoupePop();
hideCoverPop();
if (!result || result.code != web_status.SUCCESS) {
return;
}
// 与价格包一致:不立刻整页刷新,关弹窗时再回套餐 Tab
pricePackageNeedReload = true;
refreshCouponRowAfterSave(MembercoupenData);
}, true);
}
}
......@@ -693,6 +796,117 @@
this.overlay = $(obj).find('input').val();
}
// 类别切换:优先购买无券类型选项,整行(含「类型:」)隐藏;搜索须带 couType=101
function syncCoupeTypeRow(typeRadio) {
window.typeRadio = typeRadio;
$('.coupe-select-row').show();
if (String(typeRadio) === '3') {
// 优先券固定 101;隐藏类型行后必须覆盖,否则会沿用满减/代金导致搜不到
window.typeCoupe = 101;
this.typeCoupe = 101;
$('.alltrack_type_row').hide();
$('.alltrack_limit').show();
// $('.goods_track').hide(); // 折扣券已注释隐藏
} else {
// 离开优先购时清掉 101,避免非优先类别被错误过滤
if (String(window.typeCoupe) === '101') {
window.typeCoupe = '';
this.typeCoupe = '';
}
$('.alltrack_type_row').show();
$('.alltrack_radio').show();
$('.alltrack_limit').hide();
// 商品类别下原展示折扣券(goods_track),后端不支持 couType=4,已注释选项,此处不再切换显示
// if (String(typeRadio) === '2') {
// $('.goods_track').show();
// } else {
// $('.goods_track').hide();
// }
$('.goods_track').hide();
}
}
function showCoupePop() {
// Bootstrap 弹窗 enforceFocus 会抢走外层输入框焦点,导致选券/数量无法输入
$(document).off('focusin.bs.modal');
$('.coupe_pop').appendTo('body').css('display', '').addClass('is-open');
setTimeout(function () {
$('#remoteSearch').trigger('focus');
}, 50);
}
function hideCoupePop() {
$('.coupe_pop').removeClass('is-open').css('display', 'none');
$('#remoteSearch_ul').remove();
}
function showCoverPop() {
$(document).off('focusin.bs.modal');
$('.cover_pop').appendTo('body').css('display', '').addClass('is-open');
}
function hideCoverPop() {
$('.cover_pop').removeClass('is-open').css('display', 'none');
}
// 清掉 loading / layer / 自定义弹层残留黑影(价格包弹窗内最容易叠遮罩)
function clearUiShade() {
hideCoupePop();
hideCoverPop();
try { $.unblockUI(); } catch (e) {}
try { $.modal.closeLoading(); } catch (e) {}
$('body > .blockUI').remove();
$('.blockUI').remove();
// 无对话框时清掉孤儿 shade;有对话框时留给 layer 自己管
if (!$('.layui-layer-dialog:visible, .layui-layer-page:visible, .layui-layer-iframe:visible').length) {
$('.layui-layer-shade').remove();
}
}
/**
* 价格包弹窗内保存:用 msg 轻提示,避免 alertSuccess 黑遮罩叠在 Bootstrap modal 上关不掉
* @param asJson true=JSON body(券),false=表单(权益/价格)
*/
function packageSave(url, data, callback, asJson) {
var config = {
url: url,
type: 'post',
dataType: 'json',
data: data,
beforeSend: function () {
$.modal.loading('正在处理中,请稍后...');
},
success: function (result) {
clearUiShade();
if (typeof callback === 'function') {
callback(result);
}
if (!result) {
$.modal.msgError('请求失败');
return;
}
if (result.code == web_status.SUCCESS) {
$.modal.msgSuccess(result.msg || '操作成功');
} else if (result.code == web_status.WARNING) {
$.modal.msgWarning(result.msg);
} else {
$.modal.msgError(result.msg || '操作失败');
}
},
error: function () {
clearUiShade();
$.modal.msgError('请求失败');
},
complete: function () {
clearUiShade();
}
};
if (asJson) {
config.contentType = 'application/json';
}
$.ajax(config);
}
// 类别
function radioType (obj) {
var typeRadio = $(obj).find('input').val();
......@@ -700,28 +914,17 @@
$(obj).find('input').attr('checked', true);
this.typeRadio = typeRadio;
window.typeRadio = typeRadio;
$('#remoteSearch').val('');
$('.user_rules').hide();
if (typeRadio == 3) {
$('.alltrack').show();
$('.alltrack_limit').show();
$('.alltrack_radio').hide();
} else {
$('.alltrack_radio').show();
$('.alltrack').show();
$('.alltrack_limit').hide();
if (typeRadio == 2) {
$('.goods_track').show();
} else {
$('.goods_track').hide();
}
}
syncCoupeTypeRow(typeRadio);
}
// 类型
function coupetType (obj) {
var typeCoupe = $(obj).find('input').val();
this.typeCoupe = typeCoupe;
window.typeCoupe = typeCoupe;
$('#remoteSearch').val('');
$('.user_rules').hide();
if (typeCoupe == 1) {
......@@ -733,7 +936,7 @@
}
}
// 券详情
// 券详情:以券真实类别/类型同步表单,避免仍显示优先购/优先券
function coupenDetail (val, num) {
$.ajax({
url: candyfix + "/info",
......@@ -744,37 +947,102 @@
dataType: 'json',
type: 'get',
success: function (res) {
var coupenData = res.data;
var coupenData = res.data || {};
window.lastSelectedCoupon = coupenData;
if (coupenData.busiType != null) {
applyCouponTypeToForm(coupenData.busiType, coupenData.couType);
}
if (coupenData.busiType == 0) {
$('.user_rules .rules_inner').text('全部演出、商品');
} else if (coupenData.busiType == 1 || coupenData.busiType == 2) {
var coupenDataArr = [];
if (coupenData.useRules.length > 0) {
if (coupenData.useRules && coupenData.useRules.length > 0) {
for (var i = 0; i < coupenData.useRules.length; i++ ) {
coupenDataArr.push(coupenData.useRules[i].busiName);
}
coupenDataArr.join("/");
$('.user_rules .rules_inner').text(coupenDataArr.join("/"));
}
} else if (coupenData.busiType == 3) {
$('.user_rules .rules_inner').text('全部演出');
}
if (num == 3) {
if (num == 1 || num == 3) {
if (coupenData.title) {
$('#remoteSearch').val(coupenData.title);
}
$('.user_rules').show();
}
}
})
}
function applyCouponTypeToForm(busiType, couType) {
window.typeRadio = busiType;
window.typeCoupe = Number(busiType) === 3 ? 101 : couType;
this.typeRadio = window.typeRadio;
this.typeCoupe = window.typeCoupe;
var $busiInputs = $('.coupenColumn .coupeAddVal');
var $couInputs = $('.alltrack_radio .coupeAddVal');
$busiInputs.prop('checked', false).parent().removeClass('checked');
$couInputs.prop('checked', false).parent().removeClass('checked');
if ($.fn.iCheck) {
$busiInputs.iCheck('uncheck');
$couInputs.iCheck('uncheck');
}
var $busi = $('#coupeAdd' + (Number(busiType) + 1));
if ($busi.length) {
$busi.prop('checked', true).parent().addClass('checked');
if ($.fn.iCheck) {
$busi.iCheck('check');
}
}
if (Number(busiType) !== 3 && couType != null && Number(couType) !== 101) {
var $cou = $('#coupetypr' + couType);
if ($cou.length) {
$cou.prop('checked', true).parent().addClass('checked');
if ($.fn.iCheck) {
$cou.iCheck('check');
}
}
}
syncCoupeTypeRow(busiType);
}
function resolveCoupeTypeMeta() {
var busiType = $('input[name="coupeAdd_type"]:checked').val();
var couType = $('input[name="coupe_type"]:checked').val();
var selected = window.lastSelectedCoupon || {};
if ((busiType === undefined || busiType === null || busiType === '') && selected.busiType != null) {
busiType = selected.busiType;
}
if ((couType === undefined || couType === null || couType === '') && selected.couType != null) {
couType = selected.couType;
}
if (busiType === undefined || busiType === null || busiType === '') {
busiType = window.typeRadio;
}
if (couType === undefined || couType === null || couType === '') {
couType = window.typeCoupe;
}
return { busiType: busiType, couType: couType };
}
// 权益图片上传
function icon (obj, num) {
$('.coverFunNum').val(num);
var addIconIndex = $('.vip_icon').index($(obj));
if (num) {
$('.coverIndex').val(0);
} else {
var addIconIndex = $('#modalRightsArea .vip_icon').index($(obj));
if (addIconIndex < 0) {
addIconIndex = $('.vip_icon').index($(obj));
}
$('.coverIndex').val(addIconIndex);
$('.cover_pop').show();
}
// 删除券
showCoverPop();
}
function delCoupes (obj) {
var thisVal = $(obj).parents('ul');
var busiType = Number(thisVal.find('#mrcbusiType').val());
......@@ -800,7 +1068,7 @@
useScope: 90,
busiName: '演出'
}]
} else if ( busiType == 3 ) {
} else if ( busiType == 2 ) {
var couponRuleList = [{
useScope: 80,
busiName: '商品'
......@@ -814,7 +1082,7 @@
var MembercoupenData = {
busiType: busiType,
couType: couType == 3 ? 101 : couType,
couType: couType == 3 ? 101 : (busiType == 3 ? 101 : couType),
couponId: couponId,
couponRuleList: couponRuleList,
eventAmt: eventAmt,
......@@ -829,69 +1097,217 @@
valMinus: valMinus ? valMinus : 1,
oper: 2
};
$.operate.saveModalBody(prefix + "/edit_coupon", JSON.stringify(MembercoupenData));
packageSave(prefix + "/edit_coupon", JSON.stringify(MembercoupenData), function (result) {
hideCoupePop();
if (!result || result.code != web_status.SUCCESS) {
return;
}
pricePackageNeedReload = true;
thisVal.remove();
}, true);
}
function busiTypeLabel(busiType) {
if (busiType == 0) return '全场';
if (busiType == 1) return '演出';
if (busiType == 2) return '商品';
if (busiType == 3) return '优先购';
return '';
}
// 添加券
function couTypeLabel(couType) {
if (couType == 1) return '代金券';
if (couType == 2) return '满减券';
if (couType == 4) return '折扣券';
if (couType == 101) return '优先券';
return '';
}
// 加券/改券成功后就地刷新列表:优先用券详情真实类别/类型回显
function refreshCouponRowAfterSave(data) {
var mrightsId = data.mrightsId;
if (!mrightsId) {
return;
}
var $item = $('#modalRightsArea .interests_item').filter(function () {
return String(readRightsId($(this)) || '') === String(mrightsId);
}).first();
if (!$item.length) {
return;
}
function paint(row) {
var days = $('#editDays').val() || '';
var daysText = days ? (days + '天') : '天';
var $list = $item.find('.member-coupes-item');
var oper = Number(row.oper);
if (oper == 3) {
var targetId = row.operCouponId || row.couponId;
var $ul = $list.find('ul').filter(function () {
return String($(this).find('#mrccouponId').val() || '') === String(targetId)
|| String($(this).find('#mrcmcouponId').val() || '') === String(targetId);
}).first();
if ($ul.length) {
$ul.find('#mrctitle').val(row.title);
$ul.find('#mrceventAmt').val(row.eventAmt);
$ul.find('#mrcbusiType').val(row.busiType);
$ul.find('#mrccouType').val(row.couType);
$ul.find('.member-coupes-busiType').text(busiTypeLabel(row.busiType));
$ul.find('.member-coupes-type').text(couTypeLabel(row.couType));
$ul.find('.member-coupes-title').text(row.title || '');
$ul.find('.member-coupes-num').text((row.eventAmt || '') + '张');
$ul.find('.member-coupes-time').text(daysText);
$ul.find('#mrccouponId').val(row.couponId);
}
return;
}
var editData = {
busiType: row.busiType,
couType: row.couType,
title: row.title,
couponId: row.couponId,
eventAmt: row.eventAmt,
valFace: row.valFace,
valOver: row.valOver,
valMinus: row.valMinus,
overlay: row.overlay,
mcouponId: mrightsId
};
var $ul = $(''
+ '<ul><div class="member-coupes-inner"><div class="member-coupes-detail">'
+ '<input type="hidden" id="mrcmcouponId" value="' + mrightsId + '"/>'
+ '<input type="hidden" id="mrccouponId" value="' + (row.couponId || '') + '"/>'
+ '<input type="hidden" id="mrcbusiType" value="' + row.busiType + '"/>'
+ '<input type="hidden" id="mrccouType" value="' + row.couType + '"/>'
+ '<input type="hidden" id="mrctitle" value=""/>'
+ '<input type="hidden" id="mrceventAmt" value="' + (row.eventAmt || '') + '"/>'
+ '<input type="hidden" id="mrcdays" class="mrcdays" value="' + days + '"/>'
+ '<input type="hidden" id="mrvalFace" value="' + (row.valFace || '') + '"/>'
+ '<input type="hidden" id="mrcoverlay" value="' + (row.overlay || 1) + '"/>'
+ '<input type="hidden" id="mrcvalMinus" value="' + (row.valMinus || '') + '"/>'
+ '<input type="hidden" id="mrcvalOver" value="' + (row.valOver || '') + '"/>'
+ '<span class="member-coupes-mcouponId" hidden>' + mrightsId + '</span>'
+ '<span class="member-coupes-busiType"></span> '
+ '<span class="member-coupes-type"></span> '
+ '<span class="member-coupes-title"></span> '
+ '<span class="member-coupes-mark">通用</span> '
+ '<span class="member-coupes-num"></span> '
+ '<span class="member-coupes-time package-days-text"></span>'
+ '</div><div class="member-coupes-bth">'
+ '<span class="js-edit-coupes">编辑</span>'
+ '<span class="delCoupes" onclick="delCoupes(this)">删除</span>'
+ '</div></div></ul>');
$ul.find('#mrctitle').val(row.title || '');
$ul.find('.member-coupes-busiType').text(busiTypeLabel(row.busiType));
$ul.find('.member-coupes-type').text(couTypeLabel(row.couType));
$ul.find('.member-coupes-title').text(row.title || '');
$ul.find('.member-coupes-num').text((row.eventAmt || '') + '张');
$ul.find('.member-coupes-time').text(daysText);
$ul.find('.js-edit-coupes').on('click', function () {
editCoupes(this, editData);
});
$list.append($ul);
}
var row = $.extend({}, data);
var selected = window.lastSelectedCoupon || {};
if (selected.couponId && String(selected.couponId) === String(data.couponId)) {
if (selected.busiType != null) row.busiType = selected.busiType;
if (selected.couType != null) row.couType = selected.couType;
if (selected.title) row.title = selected.title;
paint(row);
return;
}
if (!data.couponId) {
paint(row);
return;
}
$.ajax({
url: candyfix + "/info",
data: { couponId: data.couponId },
dataType: 'json',
type: 'get',
success: function (res) {
var c = (res && res.data) || {};
if (c.busiType != null) row.busiType = c.busiType;
if (c.couType != null) row.couType = c.couType;
if (c.title) row.title = c.title;
paint(row);
},
error: function () {
paint(row);
}
});
}
// 添加券(须先保存权益拿到 mrightsId)
function addCoupes (obj) {
var addItemIndex = $('.member-coupes').index($(obj));
console.log(addItemIndex)
if(addItemIndex < memberRightsList.length){
$('.parentsIndex').val(addItemIndex);
$('.pop_oper').val(1);
$('.coupe_pop').show();
var item = $(obj).closest('.interests_item');
var mrightsId = readRightsId(item);
if (!mrightsId) {
$.modal.msgWarning('请先保存本条权益,再添加券');
return;
}
this.mrightsId = mrightsId;
this.couponId = '';
window.couponId = '';
window.lastSelectedCoupon = null;
$('#eventAmt').val('1');
$('#remoteSearch').val('').attr('data-name', '').attr('data-id', '');
$('.user_rules').hide();
// 每次新增加券重置类别,避免沿用上次「优先购」
$('.coupeAddVal').prop('checked', false).parent().removeClass('checked');
$('#coupeAdd1').prop('checked', true).parent().addClass('checked');
window.typeRadio = 0;
window.typeCoupe = '';
this.typeRadio = 0;
this.typeCoupe = '';
syncCoupeTypeRow(0);
$('.pop_oper').val(1);
showCoupePop();
}
// 编辑券
function editCoupes (obj, dataObj) {
var coupenObj = dataObj;
this.typeRadio = coupenObj.busiType;
this.typeCoupe = coupenObj.couType;
$('.coupeAddVal').parent().removeClass('checked');
$('.coupeAddVal').attr('checked', false);
$('#coupeAdd_title').val(coupenObj.title);
$('#coupeAdd' + (coupenObj.busiType + 1)).attr('checked', true);
$('#coupeAdd' + (coupenObj.busiType + 1)).parent().addClass('checked');
$('#coupetypr' + coupenObj.couType).attr('checked', true);
$('#coupetypr' + coupenObj.couType).parent().addClass('checked');
var parentObj = $(obj).parent().parent().parent();
var couponId = parentObj.find('#mrccouponId').val();
var eventAmt = parentObj.find('#mrceventAmt').val();
var memberId = member.memberId;
var mrightsId = parentObj.find('#mrcmcouponId').val();
var valFace = parentObj.find('#mrvalFace').val();
var valOver = parentObj.find('#mrcvalOver').val();
var valMinus = parentObj.find('#mrcvalMinus').val();
var coupenObj = dataObj || {};
var parentObj = $(obj).closest('ul');
var couponId = parentObj.find('#mrccouponId').val() || coupenObj.couponId;
var eventAmt = parentObj.find('#mrceventAmt').val() || coupenObj.eventAmt;
var mrightsId = parentObj.find('#mrcmcouponId').val() || coupenObj.mcouponId;
var valFace = parentObj.find('#mrvalFace').val() || coupenObj.valFace;
var valOver = parentObj.find('#mrcvalOver').val() || coupenObj.valOver;
var valMinus = parentObj.find('#mrcvalMinus').val() || coupenObj.valMinus;
var busiType = parentObj.find('#mrcbusiType').val();
var couType = parentObj.find('#mrccouType').val();
if (busiType === undefined || busiType === null || busiType === '') {
busiType = coupenObj.busiType;
}
if (couType === undefined || couType === null || couType === '') {
couType = coupenObj.couType;
}
busiType = Number(busiType);
couType = Number(couType);
if (this.typeCoupe == 4) {
$('#coupetypr4').parent().parent().parent().show();
} else {
$('#coupetypr4').parent().parent().parent().hide();
}
$('#coupeAdd_title').val(coupenObj.title || parentObj.find('#mrctitle').val() || '');
applyCouponTypeToForm(busiType, couType);
window.lastSelectedCoupon = {
couponId: couponId,
busiType: busiType,
couType: couType,
title: coupenObj.title || parentObj.find('#mrctitle').val() || ''
};
this.mrightsId = mrightsId;
this.couponId = couponId;
this.operCouponId = couponId;
window.couponId = couponId;
$('#eventAmt').val(eventAmt);
$('#valOver').val(valOver);
$('#valMinus').val(valMinus);
$('#valFace').val(valFace);
if (this.typeRadio == 3) {
$('.alltrack').show();
$('.alltrack_limit').show();
$('.alltrack_radio').hide();
} else {
$('.alltrack').show();
$('.alltrack_limit').hide();
}
if (this.typeCoupe == 1) {
if (couType == 1) {
$('.coupetypr_price').show();
$('.coupetypr_discount').hide();
} else {
......@@ -899,50 +1315,69 @@
$('.coupetypr_discount').show();
}
if (this.typeRadio == 0) {
var couponRuleList = [{
useScope: 100,
busiName: '全场'
}]
} else if ( this.typeRadio == 1) {
var couponRuleList = [{
useScope: 90,
busiName: '演出'
}]
} else if ( this.typeRadio == 2) {
var couponRuleList = [{
useScope: 80,
busiName: '商品'
}]
} else {
var couponRuleList = [{
useScope: 90,
busiName: '优先购'
}]
}
$('.pop_oper').val(3);
coupenDetail(couponId, 3);
$('.coupe_pop').show();
showCoupePop();
}
// 添加权益
// 添加权益(弹窗内)
function addColumn (obj) {
let appendDom = $(obj).parents('.interests_column:last');
appendDom.append(appendDom.find('.interests_item').prop('outerHTML'));
let newDom = $('.interests_item:last');
var priceId = $('#editMemberPriceId').val();
if (!priceId) {
$.modal.msgWarning('请先保存价格包');
return;
}
let appendDom = $('#modalRightsArea');
appendDom.append($('#rights-empty-template').html());
let newDom = appendDom.find('.interests_item:last');
applyRightsId(newDom, '');
newDom.find('.interestsIcon').attr('src', '');
newDom.find('#interestsTitle').val('');
newDom.find('#interestsSubTitle').val('');
newDom.find('#interestsDesc').val('');
newDom.find('.member-coupes-item').html('');
// $.operate.save(prefix + "/create", data);
syncPackageDaysText();
}
function readRightsId($item) {
if (!$item || !$item.length) {
return '';
}
var id = $item.attr('data-mrights-id')
|| $item.data('mrightsId')
|| $item.find('.js-mrights-id').val()
|| '';
id = $.trim(String(id));
return (!id || id === 'undefined' || id === 'null') ? '' : id;
}
function applyRightsId($item, mrightsId) {
if (!$item || !$item.length) {
return;
}
mrightsId = mrightsId ? String(mrightsId) : '';
$item.attr('data-mrights-id', mrightsId);
$item.data('mrightsId', mrightsId);
$item.find('.js-mrights-id').val(mrightsId);
var $inner = $item.closest('.interests_inner');
if ($inner.length) {
$inner.attr('data-mrights-id', mrightsId);
$inner.data('mrightsId', mrightsId);
}
}
// 保存权益/删除权益
function saveColumn(obj, num) {
var objDom = $(obj).parent().parent();
var domIndex = $('.interests_item').index(objDom);
var objDom = $(obj).closest('.interests_item');
var priceId = $('#editMemberPriceId').val();
if (!priceId) {
$.modal.msgWarning('请先保存价格包');
return;
}
if (objDom.data('rightsSaving')) {
return;
}
var mrightsId = readRightsId(objDom);
var postData = [];
postData.push({
"name": "cover",
......@@ -955,7 +1390,7 @@
"value": member.memberId
}, {
"name": "seqNo",
"value": domIndex
"value": $('#modalRightsArea .interests_item').index(objDom)
}, {
"name": "state",
"value": num
......@@ -965,24 +1400,44 @@
}, {
"name": "title",
"value": objDom.find('#interestsTitle').val()
}, {
"name": "memberPriceId",
"value": priceId
});
if (num == 2) {
if (objDom.parent().prop("className").indexOf('interests_inner') != -1) {
postData.push({"name": "mrightsId", "value" : memberRightsList[domIndex].mrightsId});
objDom.remove();
$.operate.saveModal(prefix + "/edit_rights", postData, function () {
window.location.reload();
if (mrightsId) {
postData.push({"name": "mrightsId", "value" : mrightsId});
objDom.data('rightsSaving', true);
packageSave(prefix + "/edit_rights", postData, function (result) {
objDom.data('rightsSaving', false);
if (!result || result.code != web_status.SUCCESS) {
return;
}
pricePackageNeedReload = true;
objDom.closest('.interests_inner').remove();
});
} else {
objDom.remove();
objDom.closest('.interests_inner').remove();
}
} else {
if (memberRightsList[domIndex]) {
postData.push({"name": "mrightsId", "value" : memberRightsList[domIndex].mrightsId});
if (mrightsId) {
postData.push({"name": "mrightsId", "value" : mrightsId});
}
objDom.data('rightsSaving', true);
packageSave(prefix + "/edit_rights", postData, function (result) {
objDom.data('rightsSaving', false);
if (!result || result.code != web_status.SUCCESS) {
return;
}
pricePackageNeedReload = true;
var rights = result.data || {};
var newId = rights.mrightsId || rights.mRightsId || '';
if (newId) {
applyRightsId(objDom, newId);
} else {
$.modal.msgWarning('权益已保存,但未返回权益ID,请关闭弹窗后重新打开再添加券');
}
$.operate.saveModal(prefix + "/edit_rights", postData, function () {
window.location.reload();
});
}
}
......@@ -1050,14 +1505,186 @@
}
}
// 保存价格
var pricePackageNeedReload = false;
// 保存价格(有 memberPriceId 则更新,空则新增)
function reloadOnMembershipTab() {
window.localStorage.setItem('tabActive', '1');
window.location.reload();
}
function findPriceById(memberPriceId) {
var list = memberPriceList || [];
for (var i = 0; i < list.length; i++) {
if (String(list[i].memberPriceId) === String(memberPriceId)) {
return list[i];
}
}
return null;
}
function updatePriceRowState(memberPriceId, state) {
var mp = findPriceById(memberPriceId);
if (mp) {
mp.state = state;
}
var $tr = $('#membership table tbody tr').filter(function () {
return $.trim($(this).children('td').eq(0).text()) === String(memberPriceId);
});
if (!$tr.length) {
return;
}
var statusHtml = state === 1
? '<span class="badge badge-primary">上线中</span>'
: '<span class="badge">已下线</span>';
var opHtml = '<a class="btn btn-xs btn-info" href="javascript:void(0)" onclick="openPriceModal(\'edit\', findPriceById(\'' + memberPriceId + '\'))">编辑</a> ';
if (state === 1) {
opHtml += '<a class="btn btn-xs btn-danger" href="javascript:void(0)" onclick="offlinePrice(\'' + memberPriceId + '\')">下线</a>';
} else {
opHtml += '<a class="btn btn-xs btn-warning" href="javascript:void(0)" onclick="onlinePrice(\'' + memberPriceId + '\')">重新上线</a>';
}
$tr.children('td').eq(6).html(statusHtml);
$tr.children('td').eq(7).html(opHtml);
}
function submitMemberShip () {
var wasNew = !$('#editMemberPriceId').val();
var MemberShipData = $('#form-membership-edit').serializeArray();
MemberShipData.push({"name": "memberId", "value": member.memberId});
// console.log(MemberShipData);
$.operate.saveModal(prefix + "/edit_price", MemberShipData);
MemberShipData.push({"name": "state", "value": 1});
packageSave(prefix + "/edit_price", MemberShipData, function (result) {
if (!result || result.code != web_status.SUCCESS) {
return;
}
// 列表等关闭弹窗时再刷新,避免整页跳走
pricePackageNeedReload = true;
var mp = result.data || {};
if (mp.memberPriceId) {
$('#editMemberPriceId').val(mp.memberPriceId);
$('#editPriceName').val(mp.name || $('#editPriceName').val());
$('#editPrice').val(mp.price != null ? mp.price : $('#editPrice').val());
$('#editPriceFixed').val(mp.priceFixed != null ? mp.priceFixed : $('#editPriceFixed').val());
$('#editPriceSpecial').val(mp.priceSpecial != null ? mp.priceSpecial : $('#editPriceSpecial').val());
$('#editDays').val(mp.days != null ? mp.days : $('#editDays').val());
$('#pricePackageModalTitle').text('编辑价格包 · ' + ($('#editPriceName').val() || '') + ' (' + mp.memberPriceId + ')');
if (wasNew) {
loadRightsIntoModal(mp.memberPriceId);
} else {
syncPackageDaysText();
setRightsEditable(true);
}
}
});
}
function syncPackageDaysText() {
var days = $('#editDays').val() || '';
$('#modalRightsArea .mrcdays').val(days);
$('#modalRightsArea .package-days-text').text(days ? (days + '天') : '天');
}
function setRightsEditable(enabled) {
if (enabled) {
$('#modalRightsLockTip').hide();
$('#modalRightsArea').css({opacity: 1, 'pointer-events': 'auto'});
$('#modalRightsHint').text('(自动绑定当前价格包)');
} else {
$('#modalRightsLockTip').show();
$('#modalRightsArea').empty().css({opacity: 0.45, 'pointer-events': 'none'});
$('#modalRightsHint').text('');
}
}
function loadRightsIntoModal(memberPriceId) {
var area = $('#modalRightsArea');
area.empty();
if (!memberPriceId) {
setRightsEditable(false);
return;
}
setRightsEditable(true);
var matched = $('#rights-store .rights-store-item').filter(function () {
return String($(this).attr('data-price-id') || '') === String(memberPriceId);
});
if (matched.length) {
matched.each(function () {
area.append($(this).clone(true, true));
});
} else {
area.append($('#rights-empty-template').html());
area.find('.interests_item:last').attr('data-mrights-id', '');
}
syncPackageDaysText();
}
function openPriceModal(mode, mp) {
pricePackageNeedReload = false;
if (mode === 'add') {
$('#pricePackageModalTitle').text('新增价格包');
$('#editMemberPriceId').val('');
$('#editPriceName').val('');
$('#editPrice').val('');
$('#editPriceFixed').val('');
$('#editPriceSpecial').val('');
$('#editDays').val('');
loadRightsIntoModal('');
} else {
mp = mp || {};
$('#pricePackageModalTitle').text('编辑价格包 · ' + (mp.name || '') + ' (' + (mp.memberPriceId || '') + ')');
$('#editMemberPriceId').val(mp.memberPriceId || '');
$('#editPriceName').val(mp.name || '');
$('#editPrice').val(mp.price != null ? mp.price : '');
$('#editPriceFixed').val(mp.priceFixed != null ? mp.priceFixed : '');
$('#editPriceSpecial').val(mp.priceSpecial != null ? mp.priceSpecial : '');
$('#editDays').val(mp.days != null ? mp.days : '');
loadRightsIntoModal(mp.memberPriceId || '');
}
$('#pricePackageModal').modal('show');
}
// 弹窗关闭时:若保存过价格包,刷新列表并留在套餐 Tab
$('#pricePackageModal').on('shown.bs.modal', function () {
// 允许加券/上传等外层弹层内的 input 获得焦点
$(document).off('focusin.bs.modal');
});
$('#pricePackageModal').on('hidden.bs.modal', function () {
clearUiShade();
if (pricePackageNeedReload) {
pricePackageNeedReload = false;
reloadOnMembershipTab();
}
});
function offlinePrice(memberPriceId) {
$.modal.confirm("确认下线该价格包?", function () {
var data = [
{"name": "memberId", "value": member.memberId},
{"name": "memberPriceId", "value": memberPriceId},
{"name": "state", "value": 2},
{"name": "price", "value": 0.01},
{"name": "priceFixed", "value": 0.01},
{"name": "priceSpecial", "value": 0.01},
{"name": "days", "value": 1}
];
packageSave(prefix + "/edit_price", data, function () {
updatePriceRowState(memberPriceId, 2);
});
});
}
function onlinePrice(memberPriceId) {
$.modal.confirm("确认重新上线该价格包?", function () {
var data = [
{"name": "memberId", "value": member.memberId},
{"name": "memberPriceId", "value": memberPriceId},
{"name": "state", "value": 1}
];
packageSave(prefix + "/edit_price", data, function () {
updatePriceRowState(memberPriceId, 1);
});
});
}
</script>
</body>
......
......@@ -178,6 +178,56 @@
</div>
<div class="row" style="margin-top: 10px;">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title ibox-title-gray dashboard-header">
<h5>优先券使用情况</h5>
</div>
<div class="ibox-content">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>券ID</th>
<th>姓名</th>
<th>手机号</th>
<th>专享标识</th>
<th>用户券状态</th>
<th>券标题</th>
<th>券类型</th>
<th>发放时间</th>
<th>到期时间</th>
<th>使用时间</th>
</tr>
</thead>
<tbody>
<tr th:each="coupon : ${orderDetailsVo.priorityCouponList}">
<td th:text="${coupon.ucouponId}"></td>
<td th:text="${coupon.name}"></td>
<td th:text="${coupon.mobile}"></td>
<td th:text="${coupon.exclusiveLabel}"></td>
<td>
<span class="badge badge-success" th:if="${coupon.state == 1}" th:text="${coupon.stateLabel}"></span>
<span class="badge badge-danger" th:if="${coupon.state == 5}" th:text="${coupon.stateLabel}"></span>
<span class="badge badge-warning" th:if="${coupon.state == 3}" th:text="${coupon.stateLabel}"></span>
<span class="badge badge-inverse" th:if="${coupon.state != 1 and coupon.state != 5 and coupon.state != 3}" th:text="${coupon.stateLabel}"></span>
</td>
<td th:text="${coupon.title}"></td>
<td th:text="${coupon.couTypeLabel}"></td>
<td th:text="${coupon.bindAt != null ? #temporals.format(coupon.bindAt, 'yyyy-MM-dd HH:mm:ss') : '-'}"></td>
<td th:text="${coupon.duedAt != null ? #temporals.format(coupon.duedAt, 'yyyy-MM-dd HH:mm:ss') : '-'}"></td>
<td th:text="${coupon.usedAt != null ? #temporals.format(coupon.usedAt, 'yyyy-MM-dd HH:mm:ss') : '-'}"></td>
</tr>
<tr th:if="${orderDetailsVo.priorityCouponList == null or #lists.isEmpty(orderDetailsVo.priorityCouponList)}">
<td colspan="10" class="text-center">暂无优先券记录</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
<th:block th:include="include :: footer" />
......
......@@ -6,5 +6,5 @@ import com.liquidnet.service.adam.entity.AdamMemberPrice;
public interface IAdamMemberPriceAdminService extends IService<AdamMemberPrice> {
boolean edit(AdamMemberPriceBuildParam param);
AdamMemberPrice edit(AdamMemberPriceBuildParam param);
}
......@@ -6,5 +6,5 @@ import com.liquidnet.service.adam.entity.AdamMemberRights;
public interface IAdamMemberRightsAdminService extends IService<AdamMemberRights> {
boolean edit(AdamMemberRightsBuildParam param);
AdamMemberRights edit(AdamMemberRightsBuildParam param);
}
......@@ -18,9 +18,12 @@ import com.liquidnet.service.adam.dto.vo.AdamMemberOrderExtVo;
import com.liquidnet.service.adam.dto.vo.AdamMemberOrderVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.dto.vo.AdamUserMemberVo;
import com.liquidnet.service.adam.dto.vo.admin.AdamMemberPriorityCouponAdminVo;
import com.liquidnet.service.adam.dto.vo.admin.MemberOrderDetailAdminVo;
import com.liquidnet.service.adam.entity.*;
import com.liquidnet.service.adam.mapper.*;
import com.liquidnet.service.candy.dto.admin.CandyPriorityCouponUsageDto;
import com.liquidnet.service.candy.mapper.CandyUserCouponMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
......@@ -63,6 +66,8 @@ public class AdamMemberOrderAdminServiceImpl extends ServiceImpl<AdamMemberOrder
private AdamMemberOrderExtMapper adamMemberOrderExtMapper;
@Autowired
private AdamMemberRefundMapper adamMemberRefundMapper;
@Autowired
private CandyUserCouponMapper candyUserCouponMapper;
@Autowired
private RedisDataSourceUtil redisDataSourceUtil;
......@@ -124,9 +129,92 @@ public class AdamMemberOrderAdminServiceImpl extends ServiceImpl<AdamMemberOrder
detailAdminVo.setAdamUserInfoVo(adamUserInfoVo);
detailAdminVo.setAdamUser(adamUser);
detailAdminVo.setAdamMemberOrderExtVo(AdamMemberOrderExtVo.getNew().copy(adamMemberOrderExt));
detailAdminVo.setPriorityCouponList(buildPriorityCouponList(adamMemberOrderVo.getUid()));
return detailAdminVo;
}
private List<AdamMemberPriorityCouponAdminVo> buildPriorityCouponList(String uid) {
List<AdamMemberPriorityCouponAdminVo> result = new ArrayList<>();
if (uid == null || uid.isEmpty()) {
return result;
}
List<CandyPriorityCouponUsageDto> usageList = candyUserCouponMapper.selectPriorityCouponUsageByUid(uid);
if (CollectionUtils.isEmpty(usageList)) {
return result;
}
for (CandyPriorityCouponUsageDto usage : usageList) {
AdamMemberPriorityCouponAdminVo vo = new AdamMemberPriorityCouponAdminVo();
vo.setUcouponId(usage.getUcouponId());
vo.setName(usage.getName());
vo.setMobile(usage.getMobile());
vo.setExclusive(usage.getExclusive());
vo.setExclusiveLabel(resolveExclusiveLabel(usage.getExclusive()));
vo.setState(usage.getState());
vo.setStateLabel(resolveCouponStateLabel(usage.getState()));
vo.setTitle(usage.getTitle());
vo.setCouType(usage.getCouType());
vo.setCouTypeLabel(resolveCouTypeLabel(usage.getCouType()));
vo.setBindAt(usage.getBindAt());
vo.setDuedAt(usage.getDuedAt());
vo.setUsedAt(usage.getUsedAt());
result.add(vo);
}
return result;
}
private String resolveExclusiveLabel(Integer exclusive) {
if (null == exclusive) {
return "-";
}
if (exclusive == 1) {
return "会员礼包";
}
if (exclusive == 0) {
return "常规";
}
return String.valueOf(exclusive);
}
private String resolveCouponStateLabel(Integer state) {
if (null == state) {
return "-";
}
switch (state) {
case 1:
return "可用";
case 2:
return "无效";
case 3:
return "已过期";
case 5:
return "已使用";
case 9:
return "移除不展示(过期或已使用)";
default:
return String.valueOf(state);
}
}
private String resolveCouTypeLabel(Integer couType) {
if (null == couType) {
return "-";
}
switch (couType) {
case 1:
return "代金卷";
case 2:
return "满减卷";
case 3:
return "兑换卷";
case 4:
return "折扣卷";
case 101:
return "优先卷";
default:
return String.valueOf(couType);
}
}
@Override
@Transactional
public AjaxResult refundProcessing(AdamMemberOrderRefundParam param, AdamMemberOrder memberOrder) {
......
package com.liquidnet.client.admin.zhengzai.adam.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamMemberPriceAdminService;
......@@ -10,18 +9,19 @@ import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.admin.AdamMemberPriceBuildParam;
import com.liquidnet.service.adam.dto.vo.AdamMemberPriceVo;
import com.liquidnet.service.adam.entity.AdamMember;
import com.liquidnet.service.adam.entity.AdamMemberPrice;
import com.liquidnet.service.adam.mapper.AdamMemberMapper;
import com.liquidnet.service.adam.mapper.AdamMemberPriceMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
......@@ -29,84 +29,146 @@ public class AdamMemberPriceAdminServiceImpl extends ServiceImpl<AdamMemberPrice
@Autowired
private AdamMemberPriceMapper adamMemberPriceMapper;
@Autowired
private AdamMemberMapper adamMemberMapper;
@Autowired
private RedisDataSourceUtil redisDataSourceUtil;
// public AdamMemberPriceVo add(AdamMemberPrice info) {
// List<AdamMemberPrice> existList = adamMemberPriceMapper.selectList(
// Wrappers.lambdaQuery(AdamMemberPrice.class).eq(AdamMemberPrice::getMemberId, info.getMemberId()));
// info.setMemberPriceId(CollectionUtils.isEmpty(existList) ? "101" : IDGenerator.nextSnowId().toString());
// info.setCreatedAt(LocalDateTime.now());
//
// adamMemberPriceMapper.insert(info);
//
// AdamMemberPriceVo vo = AdamMemberPriceVo.getNew().copy(info);
//
// redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_MEMBER_PRICE.concat(vo.getMemberPriceId()), vo);
//
// return vo;
// }
@Override
@Transactional
public boolean edit(AdamMemberPriceBuildParam param) {
public AdamMemberPrice edit(AdamMemberPriceBuildParam param) {
String memberId = param.getMemberId();
AdamMember updateInfo = new AdamMember();
updateInfo.setIntegralRate(param.getIntegralRate());
LambdaUpdateWrapper<AdamMember> memberLambdaUpdateWrapper = Wrappers.lambdaUpdate(AdamMember.class);
memberLambdaUpdateWrapper.eq(AdamMember::getState, 1);
memberLambdaUpdateWrapper.eq(AdamMember::getMemberId, param.getMemberId());
if (adamMemberMapper.update(updateInfo, memberLambdaUpdateWrapper) > 0) {
LambdaQueryWrapper<AdamMemberPrice> memberPriceLambdaQueryWrapper = Wrappers.lambdaQuery(AdamMemberPrice.class);
// memberPriceLambdaQueryWrapper.eq(AdamMemberPrice::getMemberPriceId, memberPriceId);
memberPriceLambdaQueryWrapper.eq(AdamMemberPrice::getMemberId, memberId);
memberPriceLambdaQueryWrapper.eq(AdamMemberPrice::getState, 1);
List<AdamMemberPrice> memberPriceList = adamMemberPriceMapper.selectList(memberPriceLambdaQueryWrapper);
LocalDateTime nowTime = LocalDateTime.now();
boolean saveOrUpdateRst;
Integer targetState = null == param.getState() ? 1 : param.getState();
// 下线指定价格包
if (Integer.valueOf(2).equals(targetState)) {
if (StringUtils.isBlank(param.getMemberPriceId())) {
throw new LiquidnetServiceException("-1", "下线价格包需指定会员价格ID");
}
AdamMemberPrice offline = new AdamMemberPrice();
offline.setState(2);
offline.setUpdatedAt(nowTime);
boolean offlineRst = adamMemberPriceMapper.update(offline, Wrappers.lambdaUpdate(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId())) > 0;
if (!offlineRst) {
throw new LiquidnetServiceException("-1", "会员价格下线失败");
}
refreshMemberPriceRedis(memberId);
return adamMemberPriceMapper.selectOne(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId()));
}
// 重新上线:只改 state,不改价格字段
if (Integer.valueOf(1).equals(targetState)
&& StringUtils.isNotBlank(param.getMemberPriceId())
&& param.getPrice() == null
&& param.getPriceFixed() == null
&& param.getPriceSpecial() == null
&& param.getDays() == null) {
AdamMemberPrice online = new AdamMemberPrice();
online.setState(1);
online.setUpdatedAt(nowTime);
boolean onlineRst = adamMemberPriceMapper.update(online, Wrappers.lambdaUpdate(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId())) > 0;
if (!onlineRst) {
throw new LiquidnetServiceException("-1", "会员价格重新上线失败");
}
refreshMemberPriceRedis(memberId);
return adamMemberPriceMapper.selectOne(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId()));
}
AdamMemberPrice saveOrUpdateInfo;
if (CollectionUtils.isEmpty(memberPriceList)) {
boolean saveOrUpdateRst;
if (StringUtils.isBlank(param.getMemberPriceId())) {
// 新增价格包
List<AdamMemberPrice> existList = adamMemberPriceMapper.selectList(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.orderByDesc(AdamMemberPrice::getMemberPriceId));
String nextPriceId = nextMemberPriceId(memberId, existList);
saveOrUpdateInfo = new AdamMemberPrice();
saveOrUpdateInfo.setMemberPriceId(memberId + "01");
saveOrUpdateInfo.setMemberPriceId(nextPriceId);
saveOrUpdateInfo.setMemberId(memberId);
saveOrUpdateInfo.setState(1);
//saveOrUpdateInfo.setName();
saveOrUpdateInfo.setName(StringUtils.defaultIfBlank(param.getName(), ""));
saveOrUpdateInfo.setPrice(param.getPrice());
saveOrUpdateInfo.setPriceFixed(param.getPriceFixed());
saveOrUpdateInfo.setPriceSpecial(param.getPriceSpecial());
saveOrUpdateInfo.setDays(param.getDays());
//saveOrUpdateInfo.setDetail();
saveOrUpdateInfo.setDetail("");
saveOrUpdateInfo.setCreatedAt(nowTime);
saveOrUpdateRst = adamMemberPriceMapper.insert(saveOrUpdateInfo) > 0;
} else {
// 按 memberPriceId 更新(支持同 memberId 多价格包)
LambdaQueryWrapper<AdamMemberPrice> byPriceId = Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getMemberPriceId, param.getMemberPriceId());
AdamMemberPrice exists = adamMemberPriceMapper.selectOne(byPriceId);
if (null == exists) {
throw new LiquidnetServiceException("-1", "会员价格包不存在");
}
saveOrUpdateInfo = new AdamMemberPrice();
saveOrUpdateInfo.setUpdatedAt(nowTime);
saveOrUpdateInfo.setState(1);
if (StringUtils.isNotBlank(param.getName())) {
saveOrUpdateInfo.setName(param.getName());
}
saveOrUpdateInfo.setPrice(param.getPrice());
saveOrUpdateInfo.setPriceFixed(param.getPriceFixed());
saveOrUpdateInfo.setPriceSpecial(param.getPriceSpecial());
saveOrUpdateInfo.setDays(param.getDays());
saveOrUpdateRst = adamMemberPriceMapper.update(saveOrUpdateInfo, memberPriceLambdaQueryWrapper) > 0;
saveOrUpdateInfo = adamMemberPriceMapper.selectList(memberPriceLambdaQueryWrapper).get(0);
saveOrUpdateRst = adamMemberPriceMapper.update(saveOrUpdateInfo, byPriceId) > 0;
if (saveOrUpdateRst) {
saveOrUpdateInfo = adamMemberPriceMapper.selectOne(byPriceId);
}
}
if (saveOrUpdateRst) {
String memberPriceId = saveOrUpdateInfo.getMemberPriceId();
refreshMemberPriceRedis(memberId);
return saveOrUpdateInfo;
}
throw new LiquidnetServiceException("-1", "会员价格编辑失败");
}
private void refreshMemberPriceRedis(String memberId) {
List<AdamMemberPrice> onlineList = adamMemberPriceMapper.selectList(Wrappers.lambdaQuery(AdamMemberPrice.class)
.eq(AdamMemberPrice::getMemberId, memberId)
.eq(AdamMemberPrice::getState, 1)
.orderByAsc(AdamMemberPrice::getDays));
List<AdamMemberPriceVo> vos = CollectionUtils.isEmpty(onlineList) ? new ArrayList<>()
: onlineList.stream().map(p -> AdamMemberPriceVo.getNew().copy(p)).collect(Collectors.toList());
String mpKey = AdamRedisConst.INFO_MEMBER_PRICE.concat(memberId);
List<AdamMemberPriceVo> vos = (List<AdamMemberPriceVo>) redisDataSourceUtil.getRedisAdamUtil().get(mpKey);
vos.removeIf(r -> (r.getMemberId().equals(memberId) && r.getMemberPriceId().equals(memberPriceId)));
vos.add(AdamMemberPriceVo.getNew().copy(saveOrUpdateInfo));
redisDataSourceUtil.getRedisAdamUtil().set(mpKey, vos);
return true;
}
throw new LiquidnetServiceException("-1", "会员价格编辑失败");
private String nextMemberPriceId(String memberId, List<AdamMemberPrice> existList) {
if (CollectionUtils.isEmpty(existList)) {
return memberId + "01";
}
int maxSeq = 0;
for (AdamMemberPrice price : existList) {
String id = price.getMemberPriceId();
if (StringUtils.isBlank(id)) {
continue;
}
if (id.startsWith(memberId) && id.length() > memberId.length()) {
String suffix = id.substring(memberId.length());
try {
maxSeq = Math.max(maxSeq, Integer.parseInt(suffix));
} catch (NumberFormatException ignore) {
// ignore non-numeric suffix
}
} else {
try {
maxSeq = Math.max(maxSeq, Integer.parseInt(id));
} catch (NumberFormatException ignore) {
// ignore
}
}
}
return false;
return memberId + String.format("%02d", maxSeq + 1);
}
}
......@@ -19,8 +19,9 @@ import org.springframework.util.CollectionUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Collectors;
@Service
public class AdamMemberRightsAdminServiceImpl extends ServiceImpl<AdamMemberRightsMapper, AdamMemberRights> implements IAdamMemberRightsAdminService {
......@@ -30,7 +31,7 @@ public class AdamMemberRightsAdminServiceImpl extends ServiceImpl<AdamMemberRigh
private RedisDataSourceUtil redisDataSourceUtil;
@Override
public boolean edit(AdamMemberRightsBuildParam param) {
public AdamMemberRights edit(AdamMemberRightsBuildParam param) {
String loginName = ShiroUtils.getLoginName();
LocalDateTime now = LocalDateTime.now();
String mrightsId = param.getMrightsId();
......@@ -40,6 +41,7 @@ public class AdamMemberRightsAdminServiceImpl extends ServiceImpl<AdamMemberRigh
if (isAddFlag) {
saveOrUpdateInfo.setMrightsId(IDGenerator.nextMilliId2());
saveOrUpdateInfo.setMemberId(param.getMemberId());
saveOrUpdateInfo.setMemberPriceId(StringUtils.trimToNull(param.getMemberPriceId()));
saveOrUpdateInfo.setState(1);
saveOrUpdateInfo.setSeqNo(param.getSeqNo());
......@@ -57,57 +59,44 @@ public class AdamMemberRightsAdminServiceImpl extends ServiceImpl<AdamMemberRigh
.eq(AdamMemberRights::getMemberId, param.getMemberId())
.eq(AdamMemberRights::getMrightsId, param.getMrightsId());
saveOrUpdateInfo.setMrightsId(param.getMrightsId());
saveOrUpdateInfo.setMemberId(param.getMemberId());
saveOrUpdateInfo.setState(param.getState());
saveOrUpdateInfo.setOperator(loginName);
saveOrUpdateInfo.setUpdatedAt(now);
if (param.getState() != 2) {
saveOrUpdateInfo.setMemberPriceId(StringUtils.trimToNull(param.getMemberPriceId()));
saveOrUpdateInfo.setSeqNo(param.getSeqNo());
saveOrUpdateInfo.setTitle(param.getTitle());
saveOrUpdateInfo.setSubTitle(param.getSubTitle());
saveOrUpdateInfo.setLabel(param.getLabel());
saveOrUpdateInfo.setCover(param.getCover());
saveOrUpdateInfo.setDetail(param.getDetail());
// 允许清空绑定:用 set 显式更新可空字段
updateWrapper.set(AdamMemberRights::getMemberPriceId, StringUtils.trimToNull(param.getMemberPriceId()));
}
saveOrUpadateRst = adamMemberRightsMapper.update(saveOrUpdateInfo, updateWrapper) > 0;
}
if (saveOrUpadateRst) {
// 同步REDIS:用户端:购买会员详情页:权益列表
List<AdamMemberRightsVo> vos = (List<AdamMemberRightsVo>) redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.INFO_MEMBER_RIGHTS.concat(param.getMemberId()));
if (CollectionUtils.isEmpty(vos)) {
vos = new ArrayList<>();
}
if (isAddFlag) {
vos.add(AdamMemberRightsVo.getNew().copy(saveOrUpdateInfo));
} else {
if (param.getState() == 2) {// 移除
vos.removeIf(r -> r.getMrightsId().equals(param.getMrightsId()));
} else {
List<AdamMemberRightsVo> finalVos = vos;
int idx = IntStream.range(0, finalVos.size())
.filter(i -> finalVos.get(i).getMrightsId().equals(mrightsId))
.findFirst().orElse(-1);
if (-1 == idx) {
vos.add(AdamMemberRightsVo.getNew().copy(saveOrUpdateInfo));
} else {
AdamMemberRightsVo existVo = vos.get(idx);
existVo.setSeqNo(saveOrUpdateInfo.getSeqNo());
existVo.setTitle(saveOrUpdateInfo.getTitle());
existVo.setSubTitle(saveOrUpdateInfo.getSubTitle());
existVo.setLabel(saveOrUpdateInfo.getLabel());
existVo.setCover(saveOrUpdateInfo.getCover());
existVo.setDetail(saveOrUpdateInfo.getDetail());
vos.set(idx, existVo);
}
}
if (!saveOrUpadateRst) {
return null;
}
redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_MEMBER_RIGHTS.concat(param.getMemberId()), vos);
// 与价格包一致:从 DB 全量重载 Redis,避免增量补丁丢权益 / 缺 memberPriceId
refreshMemberRightsRedis(param.getMemberId());
return saveOrUpdateInfo;
}
return saveOrUpadateRst;
private void refreshMemberRightsRedis(String memberId) {
List<AdamMemberRights> onlineList = adamMemberRightsMapper.selectList(Wrappers.lambdaQuery(AdamMemberRights.class)
.eq(AdamMemberRights::getMemberId, memberId)
.eq(AdamMemberRights::getState, 1)
.orderByAsc(AdamMemberRights::getSeqNo));
List<AdamMemberRightsVo> vos = CollectionUtils.isEmpty(onlineList) ? new ArrayList<>()
: onlineList.stream()
.sorted(Comparator.comparing(AdamMemberRights::getSeqNo, Comparator.nullsLast(Integer::compareTo)))
.map(r -> AdamMemberRightsVo.getNew().copy(r))
.collect(Collectors.toList());
redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_MEMBER_RIGHTS.concat(memberId), vos);
}
}
......@@ -6,14 +6,14 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://${liquidnet.mysql.master.urlHostAndPort}/${liquidnet.mysql.master.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://${liquidnet.mysql.master.urlHostAndPort}/${liquidnet.mysql.master.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=10000
username: ${liquidnet.mysql.master.username}
password: ${liquidnet.mysql.master.password}
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url: jdbc:mysql://${liquidnet.mysql.slave.urlHostAndPort}/${liquidnet.mysql.slave.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://${liquidnet.mysql.slave.urlHostAndPort}/${liquidnet.mysql.slave.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=10000
username: ${liquidnet.mysql.slave.username}
password: ${liquidnet.mysql.slave.password}
# 初始连接数
......
......@@ -6,14 +6,14 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://${liquidnet.mysql.master.urlHostAndPort}/${liquidnet.mysql.master.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://${liquidnet.mysql.master.urlHostAndPort}/${liquidnet.mysql.master.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=10000
username: ${liquidnet.mysql.master.username}
password: ${liquidnet.mysql.master.password}
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url: jdbc:mysql://${liquidnet.mysql.slave.urlHostAndPort}/${liquidnet.mysql.slave.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://${liquidnet.mysql.slave.urlHostAndPort}/${liquidnet.mysql.slave.database-name}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=10000
username: ${liquidnet.mysql.slave.username}
password: ${liquidnet.mysql.slave.password}
# 初始连接数
......
......@@ -32,6 +32,11 @@ public class AdamMemberRights implements Serializable {
*/
private String memberId;
/**
* 绑定的价格包ID;空表示兼容老逻辑(该卡下任意价格包可回退使用)
*/
private String memberPriceId;
/**
* 发放状态[0-INIT|1-NORMAL|2-INVALID]
*/
......
package com.liquidnet.service.candy.dto.admin;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 优先券使用情况查询结果
*/
@Data
public class CandyPriorityCouponUsageDto implements Serializable {
private static final long serialVersionUID = 1L;
private String ucouponId;
private String name;
private String mobile;
private Integer exclusive;
private Integer state;
private String title;
private Integer couType;
private LocalDateTime bindAt;
private LocalDateTime duedAt;
private LocalDateTime usedAt;
}
......@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.candy.dto.CandyUserCouponBasicDto;
import com.liquidnet.service.candy.dto.CouponDao;
import com.liquidnet.service.candy.dto.admin.CandyPriorityCouponUsageDto;
import com.liquidnet.service.candy.entity.CandyUserCoupon;
import org.apache.ibatis.annotations.Param;
......@@ -44,4 +45,9 @@ public interface CandyUserCouponMapper extends BaseMapper<CandyUserCoupon> {
* 查询羊毛卷信息
*/
List<CouponDao> WoolRoll();
/**
* 按用户查优先券使用情况(cou_type=101)
*/
List<CandyPriorityCouponUsageDto> selectPriorityCouponUsageByUid(@Param("uid") String uid);
}
......@@ -109,4 +109,24 @@
and d.status = 6
order by a.used_at;
</select>
<select id="selectPriorityCouponUsageByUid" resultType="com.liquidnet.service.candy.dto.admin.CandyPriorityCouponUsageDto">
SELECT
cuc.ucoupon_id AS ucouponId,
auml.`name` AS `name`,
auml.mobile AS mobile,
cuc.`exclusive` AS exclusive,
cuc.state AS state,
ac.title AS title,
ac.cou_type AS couType,
cuc.bind_at AS bindAt,
cuc.dued_at AS duedAt,
cuc.used_at AS usedAt
FROM candy_user_coupon AS cuc
LEFT JOIN adam_user_mobile_locate AS auml ON auml.uid = cuc.uid
JOIN candy_coupon AS ac ON ac.coupon_id = cuc.coupon_id
WHERE cuc.uid = #{uid,jdbcType=VARCHAR}
AND ac.cou_type = 101
ORDER BY cuc.bind_at DESC, cuc.ucoupon_id DESC
</select>
</mapper>
-- 多档会员:权益绑价格包 + 老数据洗到年档
-- 月卡/体验等价格包请在 admin「会员套餐」页新增,不要用本脚本灌业务数据。
-- ========== 1) DDL(若已执行过可跳过)==========
ALTER TABLE adam_member_rights
ADD COLUMN member_price_id VARCHAR(64) NULL COMMENT '绑定价格包ID(必绑具体档;空仅历史兼容,执行下方洗数后应无空)' AFTER member_id;
CREATE INDEX idx_amember_rights_price_id ON adam_member_rights (member_price_id);
-- ========== 2) 洗数:NULL 权益绑到该会员「天数最大」的价格包(视为年档)==========
-- 若同一 member 有多条 days 并列最大,取 member_price_id 字典序最小的一条
UPDATE adam_member_rights r
INNER JOIN (
SELECT p.member_id, MIN(p.member_price_id) AS year_price_id
FROM adam_member_price p
INNER JOIN (
SELECT member_id, MAX(days) AS max_days
FROM adam_member_price
WHERE state IN (1, 2)
GROUP BY member_id
) m ON m.member_id = p.member_id AND m.max_days = p.days
WHERE p.state IN (1, 2)
GROUP BY p.member_id
) y ON y.member_id = r.member_id
SET r.member_price_id = y.year_price_id
WHERE r.member_price_id IS NULL
AND r.state = 1;
-- 检查是否还有空绑定(期望 0 行)
-- SELECT mid, mrights_id, member_id, title FROM adam_member_rights WHERE state = 1 AND member_price_id IS NULL;
-- ========== 3) Redis(会员价/权益读 Redis,Mongo 已注释不用)==========
-- 洗数后必须刷缓存,否则线上仍是旧 rights(缺 memberPriceId)。
-- 任选其一:
-- A) admin 打开会员套餐,随便保存一条该卡权益(会 refreshMemberRightsRedis)
-- B) 手工删 key(memberId 按实际替换,登登登一般是 1):
-- DEL adam:info:member:rights:1
-- DEL adam:info:member:price:1
-- (若有 V 版)DEL adam:info:member:rights:v:1
-- 下次请求会按代码从 DB/逻辑重建;价格包建议在 admin 保存一次价格以确保 price 列表正确。
-- ========== 4) 后续运营 ==========
-- admin 新增月卡/体验价格包后,在「编辑」弹窗内配置本档权益与券张数。
......@@ -353,6 +353,9 @@ alter table adam_member add limite_at datetime(3) null comment '限购结束时
alter table adam_member_price modify price_fixed decimal(8, 2) null comment '购买价格';
alter table adam_member_price add price_special decimal(8, 2) null comment '特价:首次、首年优惠价' after price_fixed;
# 多档会员:权益可绑价格包(详见 db_adam_member_price_multi_package.sql
# alter table adam_member_rights add member_price_id varchar(64) null comment '绑定价格包ID,空表示全档回退共用' after member_id;
# 会员码表调整
alter table adam_member_code modify state tinyint null comment '状态[0-可用|1-已用|2-无效]';
create index idx_amember_code_id on adam_member_code (code);
......
......@@ -24,6 +24,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ApiSupport(order = 10030)
@Api(tags = "会员信息")
......@@ -52,30 +55,61 @@ public class AdamMemberController {
// memberVo.setBuySwitch(adamRdmService.getSwitch(AdamRedisConst.SWITCH_BUY_MEMBER));
memberVo.calculateLimitMarker(adamRdmService.getMaxMemberLimitation());
memberVo.setPriceVoList(adamRdmService.getMemberPriceVoByMemberId(memberVo.getMemberId()));
String currentUid = CurrentUtil.getCurrentUid();
int rightsVFlag = 0;
if (!StringUtils.isEmpty(currentUid)) {
AdamUserMemberVo userMemberVo = adamRdmService.getUserMemberVoByUid(currentUid);
memberVo.setUserMemberVo(userMemberVo);
if (null == userMemberVo || !userMemberVo.isActive()) {
memberVo.setRightsVoList(adamRdmService.getMemberRightsVoByMemberId(memberVo.getMemberId(), 0));
} else if (userMemberVo.isActive()) {
memberVo.setRightsVoList(
userMemberVo.isOldMember() ? adamRdmService.getMemberRightsVoByMemberId(memberVo.getMemberId(), 1)
: adamRdmService.getMemberRightsVoByMemberId(memberVo.getMemberId(), 0)
);
if (null != userMemberVo && userMemberVo.isActive() && userMemberVo.isOldMember()) {
rightsVFlag = 1;
}
} else {
memberVo.setUserMemberVo(null);
memberVo.setRightsVoList(adamRdmService.getMemberRightsVoByMemberId(memberVo.getMemberId(), 0));
}
// 权益只挂在价格包下:priceVoList[].rightsVoList
memberVo.setPriceVoList(attachRightsToPriceList(
adamRdmService.getMemberPriceVoByMemberId(memberVo.getMemberId()),
adamRdmService.getMemberRightsVoByMemberId(memberVo.getMemberId(), rightsVFlag),
rightsVFlag
));
}
return ResponseDto.success(memberVo);
}
/**
* 将权益挂到对应价格包。
* vFlag=0:按 memberPriceId 精确匹配;
* vFlag=1:老会员展示用权益无价格包绑定,挂到每个价格包下。
*/
private List<AdamMemberPriceVo> attachRightsToPriceList(List<AdamMemberPriceVo> priceVoList,
List<AdamMemberRightsVo> rightsVoList,
int rightsVFlag) {
if (priceVoList == null || priceVoList.isEmpty()) {
return priceVoList;
}
List<AdamMemberRightsVo> allRights = rightsVoList == null ? Collections.emptyList() : rightsVoList;
for (AdamMemberPriceVo priceVo : priceVoList) {
if (rightsVFlag == 1) {
priceVo.setRightsVoList(allRights);
continue;
}
String priceId = priceVo.getMemberPriceId();
if (StringUtils.isEmpty(priceId)) {
priceVo.setRightsVoList(Collections.emptyList());
continue;
}
List<AdamMemberRightsVo> matched = new ArrayList<>();
for (AdamMemberRightsVo rightsVo : allRights) {
if (priceId.equals(rightsVo.getMemberPriceId())) {
matched.add(rightsVo);
}
}
priceVo.setRightsVoList(matched);
}
return priceVoList;
}
@ApiOperationSupport(order = 2)
@ApiOperation(value = "会员协议")
@GetMapping("agreement")
......
......@@ -376,7 +376,8 @@ public class AdamMemberOrderServiceImpl implements IAdamMemberOrderService {
if (now.isAfter(AdamUserMemberVo.spotTime)) {
// 权益券发放
toMqSqls.add(SqlMapping.get("candy_mgt_coupon.add_for_member"));
LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(now, userInfoVo.getMobile(), handleMemberOrderVo.getMemberId());
LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(
now, userInfoVo.getMobile(), handleMemberOrderVo.getMemberId(), handleMemberOrderVo.getMemberPriceId());
queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UMEMBER.getKey(),
SqlMapping.gets(toMqSqls, operationObjs, initMemberOrderObjs, initMemberOrderExtObjs, updateUserInfoObjs, updateUserMobileLocateObjs, initMemberRightsCouponObjs)
......@@ -576,7 +577,8 @@ public class AdamMemberOrderServiceImpl implements IAdamMemberOrderService {
// 权益券发放
toMqSqls.add(SqlMapping.get("candy_mgt_coupon.add_for_member"));
LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(now, userInfoVo.getMobile(), initMemberOrderVo.getMemberId());
LinkedList<Object[]> initMemberRightsCouponObjs = this.issueMemberRightsCouponProcessing(
now, userInfoVo.getMobile(), initMemberOrderVo.getMemberId(), initMemberOrderVo.getMemberPriceId());
s = System.currentTimeMillis();
queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UMEMBER.getKey(),
......@@ -652,27 +654,25 @@ public class AdamMemberOrderServiceImpl implements IAdamMemberOrderService {
}
/**
* 发放会员专属特权券
*
* @param now 时间
* @param mobile 用户手机号
* @param memberId 会员ID
* @return LinkedList<Object[]>
* 发放会员专属特权券(仅发放绑定当前价格包的权益)
*/
private LinkedList<Object[]> issueMemberRightsCouponProcessing(LocalDateTime now, String mobile, String memberId) {
private LinkedList<Object[]> issueMemberRightsCouponProcessing(LocalDateTime now, String mobile, String memberId, String memberPriceId) {
LinkedList<Object[]> initMemberRightsCouponObjs = CollectionUtil.linkedListObjectArr();
List<AdamMemberRightsVo> memberRightsVoList = adamRdmService.getMemberRightsVoByMemberId(memberId, 0);
memberRightsVoList = filterRightsByMemberPriceId(memberRightsVoList, memberPriceId);
if (!CollectionUtils.isEmpty(memberRightsVoList)) {
memberRightsVoList.forEach(r -> {
List<AdamMemberRightsCouponVo> memberRightsCouponVoList = adamRdmService.getMemberRightsCouponVoByRightsId(r.getMrightsId());
if (!CollectionUtils.isEmpty(memberRightsCouponVoList)) {
// 该权益下关联券统计
Map<String, Long> rightsCouponCtMap = memberRightsCouponVoList.stream()
.collect(Collectors.groupingBy(AdamMemberRightsCouponVo::getCouponId, Collectors.counting()));
rightsCouponCtMap.forEach((couponId, num) -> {
if (num <= 0) {
return;
}
initMemberRightsCouponObjs.add(
new Object[]{r.getMrightsId(), couponId, num, mobile, now, mobile, now}
);
......@@ -683,4 +683,16 @@ public class AdamMemberOrderServiceImpl implements IAdamMemberOrderService {
return initMemberRightsCouponObjs;
}
/**
* 只使用绑定当前价格包的权益(需先执行洗数把历史 NULL 绑到年档)
*/
private List<AdamMemberRightsVo> filterRightsByMemberPriceId(List<AdamMemberRightsVo> rightsVoList, String memberPriceId) {
if (CollectionUtils.isEmpty(rightsVoList) || StringUtils.isEmpty(memberPriceId)) {
return Collections.emptyList();
}
return rightsVoList.stream()
.filter(r -> memberPriceId.equals(r.getMemberPriceId()))
.collect(Collectors.toList());
}
}
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