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

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

Merge branch 'refs/heads/master' into jxl_20240313_prod

parents 0c7fd4c2 2fb244b4
-- ============================================================
-- 草莓护照 / 徽章 v1.1 数据库变更
-- 说明:新增徽章类型「签证页」(type=4),仅更新字段注释,无结构变更
-- 适用:已在 v1.0 建表的环境(adam_caomei_badge)
-- 日期:2026-05-13
-- ============================================================
-- 1. 徽章类型:补充 4-签证页
ALTER TABLE `adam_caomei_badge`
MODIFY COLUMN `type` tinyint(4) NOT NULL COMMENT '徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章, 4-签证页';
-- 2. 关联演出:签证页同样需绑定音乐节/演出
ALTER TABLE `adam_caomei_badge`
MODIFY COLUMN `performance_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '关联演出ID (演出纪念徽章、签证页必填,其他类型为空)';
...@@ -26,10 +26,10 @@ public class AdamCaomeiBadgeParam { ...@@ -26,10 +26,10 @@ public class AdamCaomeiBadgeParam {
@ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)") @ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)")
private String icon; private String icon;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章") @ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章, 4-签证页")
private Integer type; private Integer type;
@ApiModelProperty(value = "关联演出ID (仅演出纪念徽章必填,其他类型为空)") @ApiModelProperty(value = "关联演出ID (演出纪念徽章、签证页必填,其他类型为空)")
private String performanceId; private String performanceId;
@ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布") @ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布")
......
...@@ -14,7 +14,7 @@ public class AdamCaomeiBadgeSearchParam { ...@@ -14,7 +14,7 @@ public class AdamCaomeiBadgeSearchParam {
@ApiModelProperty(value = "徽章名称") @ApiModelProperty(value = "徽章名称")
private String name; private String name;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章") @ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章, 4-签证页")
private Integer type; private Integer type;
@ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布") @ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布")
......
...@@ -28,7 +28,7 @@ public class AdamCaomeiBadgeVo { ...@@ -28,7 +28,7 @@ public class AdamCaomeiBadgeVo {
@ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)") @ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)")
private String icon; private String icon;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章") @ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章, 4-签证页")
private Integer type; private Integer type;
@ApiModelProperty(value = "关联演出ID") @ApiModelProperty(value = "关联演出ID")
......
...@@ -27,10 +27,10 @@ public class AdamCaomeiPassportBadgeShelfItemVo { ...@@ -27,10 +27,10 @@ public class AdamCaomeiPassportBadgeShelfItemVo {
@ApiModelProperty("分享文案") @ApiModelProperty("分享文案")
private String shareText; private String shareText;
@ApiModelProperty("类型 1护照 2演出 3特殊") @ApiModelProperty("类型 1护照 2演出 3特殊(不含 4 签证页,签证页见 home.visaBadges)")
private Integer type; private Integer type;
@ApiModelProperty("关联演出ID(演出纪念徽章)") @ApiModelProperty("关联演出ID(演出纪念徽章、签证页)")
private String performanceId; private String performanceId;
@ApiModelProperty("关联演出名称(type=2 时用于按演出分组展示;无数据时可能为演出ID)") @ApiModelProperty("关联演出名称(type=2 时用于按演出分组展示;无数据时可能为演出ID)")
......
...@@ -13,9 +13,12 @@ public class AdamCaomeiPassportHomeVo { ...@@ -13,9 +13,12 @@ public class AdamCaomeiPassportHomeVo {
@ApiModelProperty("个人信息卡片") @ApiModelProperty("个人信息卡片")
private AdamCaomeiPassportUserCardVo userCard; private AdamCaomeiPassportUserCardVo userCard;
@ApiModelProperty("已认领徽章(全部获得记录,用于网格墙)") @ApiModelProperty("已认领徽章(用于网格墙;不含 type=4 签证页)")
private List<AdamCaomeiPassportUserClaimedBadgeVo> claimedBadges; private List<AdamCaomeiPassportUserClaimedBadgeVo> claimedBadges;
@ApiModelProperty("全部上架徽章(扁平列表;演出类含 performanceName,前端可按类型或按演出分组展示)") @ApiModelProperty("签证页卡片(type=4;home 内静默发放后返回,含 performanceName)")
private List<AdamCaomeiPassportUserClaimedBadgeVo> visaBadges;
@ApiModelProperty("全部上架徽章货架(不含 type=4 签证页;演出类含 performanceName)")
private List<AdamCaomeiPassportBadgeShelfItemVo> allBadges; private List<AdamCaomeiPassportBadgeShelfItemVo> allBadges;
} }
...@@ -9,7 +9,7 @@ import lombok.Data; ...@@ -9,7 +9,7 @@ import lombok.Data;
import java.util.Date; import java.util.Date;
@Data @Data
@ApiModel("草莓护照-已认领徽章(墙)") @ApiModel("草莓护照-用户已获徽章(徽章墙、签证页卡片等共用)")
public class AdamCaomeiPassportUserClaimedBadgeVo { public class AdamCaomeiPassportUserClaimedBadgeVo {
@ApiModelProperty("徽章ID") @ApiModelProperty("徽章ID")
...@@ -27,10 +27,13 @@ public class AdamCaomeiPassportUserClaimedBadgeVo { ...@@ -27,10 +27,13 @@ public class AdamCaomeiPassportUserClaimedBadgeVo {
@ApiModelProperty("分享文案") @ApiModelProperty("分享文案")
private String shareText; private String shareText;
@ApiModelProperty("类型 1护照类型徽章 2演出类型徽章 3特殊徽章") @ApiModelProperty("类型 1护照类型徽章 2演出类型徽章 3特殊徽章 4签证页")
private Integer type; private Integer type;
@ApiModelProperty("关联演出名称(仅 type=2 有值)") @ApiModelProperty("关联音乐节/演出ID(type=2、type=4 有值)")
private String performanceId;
@ApiModelProperty("关联音乐节/演出名称(type=2、type=4 有值)")
private String performanceName; private String performanceName;
@ApiModelProperty("获得时间") @ApiModelProperty("获得时间")
......
...@@ -17,7 +17,8 @@ public interface IAdamCaomeiPassportUserService { ...@@ -17,7 +17,8 @@ public interface IAdamCaomeiPassportUserService {
ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo); ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo);
/** /**
* 护照首页:个人信息、实名状态、已认领墙、按类型分组的全部上架徽章 * 护照首页:个人信息、实名状态、已认领墙、签证页(type=4)、上架徽章货架;
* 末尾自动发放满足条件的 type=4 签证页,并在 {@link AdamCaomeiPassportHomeVo#getVisaBadges()} 返回。
*/ */
ResponseDto<AdamCaomeiPassportHomeVo> getPassportHome(); ResponseDto<AdamCaomeiPassportHomeVo> getPassportHome();
......
...@@ -32,6 +32,16 @@ public interface IAdamUserService { ...@@ -32,6 +32,16 @@ public interface IAdamUserService {
*/ */
AdamUserInfoVo register(String mobile, int isComplete); AdamUserInfoVo register(String mobile, int isComplete);
/**
* 手机号注册(指定注册来源,写入 adam_user_mobile_locate.regist_source)
*
* @param mobile
* @param isComplete 是否标记为已完善状态[0-未完善|1-已完善]
* @param registSource 注册来源;为空时回退到请求头 source
* @return AdamUserInfoVo
*/
AdamUserInfoVo register(String mobile, int isComplete, String registSource);
/** /**
* 第三方账号注册 * 第三方账号注册
* *
......
...@@ -9,6 +9,7 @@ public class SweetConstant { ...@@ -9,6 +9,7 @@ public class SweetConstant {
public final static String REDIS_KEY_SWEET_MANUAL_NOTIFY_LIST = "sweet:manual:notify:manual:"; public final static String REDIS_KEY_SWEET_MANUAL_NOTIFY_LIST = "sweet:manual:notify:manual:";
public final static String REDIS_KEY_SWEET_MANUAL_RICH_TEXT = "sweet:manual:richText:manual:"; public final static String REDIS_KEY_SWEET_MANUAL_RICH_TEXT = "sweet:manual:richText:manual:";
public final static String REDIS_KEY_SWEET_MANUAL_SORT = "sweet:manual:sort:manual:"; public final static String REDIS_KEY_SWEET_MANUAL_SORT = "sweet:manual:sort:manual:";
public final static String REDIS_KEY_SWEET_MANUAL_EXT_CONFIG = "sweet:manual:extConfig:manual:";
public final static String REDIS_KEY_SWEET_ARTISTS_RELATION = "sweet:artists:relation:uid:"; public final static String REDIS_KEY_SWEET_ARTISTS_RELATION = "sweet:artists:relation:uid:";
public final static String REDIS_KEY_SWEET_ARTISTS_DETAILS = "sweet:artists:details:"; public final static String REDIS_KEY_SWEET_ARTISTS_DETAILS = "sweet:artists:details:";
public final static String REDIS_KEY_SWEET_SHOP = "sweet:artists:shop:manual:"; public final static String REDIS_KEY_SWEET_SHOP = "sweet:artists:shop:manual:";
......
package com.liquidnet.service.sweet.constant;
import com.liquidnet.service.sweet.vo.SweetManualSortOptionVo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 草莓音乐节手册 Tab 显示模块
*/
public enum SweetManualPosition {
ARTIST("artist", "艺人"),
SIGNING_TIME("signingTime", "签售时间"),
SITE_MAP("siteMap", "现场地图"),
HOW_TO_REACH("howToReach", "交通出行"),
OFFICIAL_SUPPORT("officialSupport", "官方支持"),
AUDIENCE_NOTICE("audienceNotice", "观众须知"),
PREVENTION_GUIDELINES("preventionGuidelines", "防疫指南"),
NOTICE("notice", "通知公告"),
STRATEGY("strategy", "观演攻略"),
CUSTOMER_SERVICE("customerService", "客服"),
FOOD_GUIDE("foodGuide", "餐饮攻略"),
ALBUM("album", "相册"),
LOST_FOUND("lostFound", "失物招领"),
MAP_GEOJSON("mapGeojson", "互动地图");
private final String key;
private final String label;
SweetManualPosition(String key, String label) {
this.key = key;
this.label = label;
}
public String getKey() {
return key;
}
public String getLabel() {
return label;
}
public static boolean isValid(String key) {
if (key == null) {
return false;
}
for (SweetManualPosition position : values()) {
if (position.key.equals(key)) {
return true;
}
}
return false;
}
public static String getLabel(String key) {
for (SweetManualPosition position : values()) {
if (position.key.equals(key)) {
return position.label;
}
}
return key;
}
public static List<SweetManualSortOptionVo> allOptions() {
return Arrays.stream(values())
.map(item -> new SweetManualSortOptionVo(item.key, item.label))
.collect(Collectors.toList());
}
public static List<String> parsePositions(String showPosition) {
if (showPosition == null || showPosition.trim().isEmpty()) {
return new ArrayList<>();
}
return Arrays.stream(showPosition.split(","))
.map(String::trim)
.filter(item -> !item.isEmpty())
.collect(Collectors.toList());
}
public static String normalizeContent(String content) {
List<String> invalidKeys = new ArrayList<>();
List<String> positions = parsePositions(content).stream()
.filter(key -> {
if (isValid(key)) {
return true;
}
invalidKeys.add(key);
return false;
})
.collect(Collectors.toList());
if (!invalidKeys.isEmpty()) {
throw new IllegalArgumentException("无效的模块标识: " + String.join(",", invalidKeys));
}
return String.join(",", positions);
}
}
package com.liquidnet.service.sweet.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel("草莓音乐节手册扩展配置参数")
public class SweetManualExtConfigParam implements Serializable {
@ApiModelProperty(value = "电子手册id", required = true)
private String manualId;
@ApiModelProperty("餐饮攻略链接")
private String foodGuideUrl;
@ApiModelProperty("相册链接")
private String albumUrl;
@ApiModelProperty("失物招领问卷星ID")
private String lostFoundWjxId;
@ApiModelProperty("地图GeoJSON数据")
private Object mapGeojson;
}
package com.liquidnet.service.sweet.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel("手册地图GeoJSON保存参数")
public class SweetManualMapGeojsonParam implements Serializable {
@ApiModelProperty(value = "电子手册id", required = true)
private String manualId;
@ApiModelProperty(value = "地图GeoJSON数据", required = true)
private Object mapGeojson;
}
package com.liquidnet.service.sweet.service;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.dto.SweetManualExtConfigDto;
import com.liquidnet.service.sweet.param.SweetManualExtConfigParam;
import com.liquidnet.service.sweet.param.SweetManualMapGeojsonParam;
public interface ISweetManualExtConfigService {
ResponseDto<SweetManualExtConfigDto> get(String manualId);
ResponseDto<Boolean> save(SweetManualExtConfigParam param);
ResponseDto<Object> getMapGeojson(String manualId);
ResponseDto<Boolean> saveMapGeojson(SweetManualMapGeojsonParam param);
}
...@@ -3,6 +3,10 @@ package com.liquidnet.service.sweet.service; ...@@ -3,6 +3,10 @@ package com.liquidnet.service.sweet.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.entity.SweetManualSort; import com.liquidnet.service.sweet.entity.SweetManualSort;
import com.liquidnet.service.sweet.vo.SweetManualSortOptionVo;
import com.liquidnet.service.sweet.vo.SweetManualSortVo;
import java.util.List;
/** /**
* <p> * <p>
...@@ -14,8 +18,10 @@ import com.liquidnet.service.sweet.entity.SweetManualSort; ...@@ -14,8 +18,10 @@ import com.liquidnet.service.sweet.entity.SweetManualSort;
*/ */
public interface ISweetManualSortService extends IService<SweetManualSort> { public interface ISweetManualSortService extends IService<SweetManualSort> {
ResponseDto<SweetManualSort> get(String manualId); ResponseDto<SweetManualSortVo> get(String manualId);
ResponseDto<Boolean> add(String manualId, String content);
ResponseDto<Boolean> add(String manualId,String content); ResponseDto<List<SweetManualSortOptionVo>> options();
} }
package com.liquidnet.service.sweet.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("手册显示模块选项")
public class SweetManualSortOptionVo implements Serializable {
@ApiModelProperty("模块标识")
private String key;
@ApiModelProperty("模块名称")
private String label;
}
package com.liquidnet.service.sweet.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@Data
@ApiModel("手册显示模块配置")
public class SweetManualSortVo implements Serializable {
@ApiModelProperty("主键")
private Long mid;
@ApiModelProperty("电子手册id")
private String manualId;
@ApiModelProperty("已选模块,逗号分隔")
private String showPosition;
@ApiModelProperty("已选模块列表(按顺序)")
private List<String> positions;
@ApiModelProperty("可选模块列表")
private List<SweetManualSortOptionVo> options;
@ApiModelProperty("创建时间")
private LocalDateTime createdAt;
@ApiModelProperty("更新时间")
private LocalDateTime updatedAt;
}
...@@ -94,11 +94,11 @@ public class AdamCaomeiBadgeController extends BaseController { ...@@ -94,11 +94,11 @@ public class AdamCaomeiBadgeController extends BaseController {
badge.setCreatedAt(new Date()); badge.setCreatedAt(new Date());
badge.setUpdatedAt(new Date()); badge.setUpdatedAt(new Date());
// 演出类型校验 // 演出纪念徽章、签证页须关联音乐节
if (badge.getType() != null && badge.getType() == 2) { if (badgeRequiresPerformance(badge.getType())) {
String pid = StringUtils.trimToEmpty(badge.getPerformanceId()); String pid = StringUtils.trimToEmpty(badge.getPerformanceId());
if (StringUtils.isBlank(pid)) { if (StringUtils.isBlank(pid)) {
return error("演出纪念徽章必须关联演出"); return error(badgePerformanceRequiredMessage(badge.getType()));
} }
badge.setPerformanceId(pid); badge.setPerformanceId(pid);
if (!adamCaomeiBadgeAdminService.kylinPerformanceExists(pid)) { if (!adamCaomeiBadgeAdminService.kylinPerformanceExists(pid)) {
...@@ -184,10 +184,10 @@ public class AdamCaomeiBadgeController extends BaseController { ...@@ -184,10 +184,10 @@ public class AdamCaomeiBadgeController extends BaseController {
badge.setUpdatedAt(new java.util.Date()); badge.setUpdatedAt(new java.util.Date());
// 徽章类型与「已发布不可改」一致:编辑时不允许变更类型 // 徽章类型与「已发布不可改」一致:编辑时不允许变更类型
badge.setType(oldBadge.getType()); badge.setType(oldBadge.getType());
if (badge.getType() != null && badge.getType() == 2) { if (badgeRequiresPerformance(badge.getType())) {
String pid = StringUtils.trimToEmpty(badge.getPerformanceId()); String pid = StringUtils.trimToEmpty(badge.getPerformanceId());
if (StringUtils.isBlank(pid)) { if (StringUtils.isBlank(pid)) {
return error("演出纪念徽章必须关联演出"); return error(badgePerformanceRequiredMessage(badge.getType()));
} }
badge.setPerformanceId(pid); badge.setPerformanceId(pid);
if (!adamCaomeiBadgeAdminService.kylinPerformanceExists(pid)) { if (!adamCaomeiBadgeAdminService.kylinPerformanceExists(pid)) {
...@@ -217,6 +217,17 @@ public class AdamCaomeiBadgeController extends BaseController { ...@@ -217,6 +217,17 @@ public class AdamCaomeiBadgeController extends BaseController {
return toAjax(adamCaomeiBadgeAdminService.updateById(updateBadge)); return toAjax(adamCaomeiBadgeAdminService.updateById(updateBadge));
} }
private static boolean badgeRequiresPerformance(Integer type) {
return type != null && (type == 2 || type == 4);
}
private static String badgePerformanceRequiredMessage(Integer type) {
if (type != null && type == 4) {
return "签证页必须关联音乐节";
}
return "演出纪念徽章必须关联演出";
}
/** /**
* @return 校验通过返回 null,否则返回错误 AjaxResult * @return 校验通过返回 null,否则返回错误 AjaxResult
*/ */
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
<option value="">请选择</option> <option value="">请选择</option>
<option value="1">护照纪念徽章</option> <option value="1">护照纪念徽章</option>
<option value="2">演出纪念徽章</option> <option value="2">演出纪念徽章</option>
<option value="4">签证页</option>
<!-- <option value="3">特殊徽章</option> --> <!-- <option value="3">特殊徽章</option> -->
</select> </select>
</div> </div>
...@@ -59,7 +60,7 @@ ...@@ -59,7 +60,7 @@
<label class="col-sm-3 control-label is-required">关联演出:</label> <label class="col-sm-3 control-label is-required">关联演出:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input name="performanceId" id="performanceId" class="form-control" type="text" placeholder="请输入演出ID"> <input name="performanceId" id="performanceId" class="form-control" type="text" placeholder="请输入演出ID">
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 演出纪念徽章必填</span> <span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 演出纪念徽章、签证页必填</span>
</div> </div>
</div> </div>
</form> </form>
...@@ -71,7 +72,7 @@ ...@@ -71,7 +72,7 @@
var platformUrl = /*[[${platformUrl}]]*/ ''; var platformUrl = /*[[${platformUrl}]]*/ '';
function typeChange(val) { function typeChange(val) {
if (val == 2) { if (val == 2 || val == 4) {
$("#ticketTimesDiv").show(); $("#ticketTimesDiv").show();
$("#performanceId").prop("required", true); $("#performanceId").prop("required", true);
} else { } else {
......
...@@ -32,10 +32,11 @@ ...@@ -32,10 +32,11 @@
<div class="col-sm-8"> <div class="col-sm-8">
<div class="form-control-static" th:if="*{type == 1}">护照纪念徽章</div> <div class="form-control-static" th:if="*{type == 1}">护照纪念徽章</div>
<div class="form-control-static" th:if="*{type == 2}">演出纪念徽章</div> <div class="form-control-static" th:if="*{type == 2}">演出纪念徽章</div>
<div class="form-control-static" th:if="*{type == 3}">特殊徽章</div> <div class="form-control-static" th:if="*{type == 4}">签证页</div>
<!-- <div class="form-control-static" th:if="*{type == 3}">特殊徽章</div>-->
</div> </div>
</div> </div>
<div class="form-group" th:style="${badge.type == 2 ? 'display:block;' : 'display:none;'}"> <div class="form-group" th:style="${badge.type == 2 or badge.type == 4 ? 'display:block;' : 'display:none;'}">
<label class="col-sm-3 control-label">关联演出:</label> <label class="col-sm-3 control-label">关联演出:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<div class="form-control-static" th:text="*{performanceId}"></div> <div class="form-control-static" th:text="*{performanceId}"></div>
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
<select class="form-control m-b" disabled> <select class="form-control m-b" disabled>
<option th:selected="${badge.type == 1}" value="1">护照纪念徽章</option> <option th:selected="${badge.type == 1}" value="1">护照纪念徽章</option>
<option th:selected="${badge.type == 2}" value="2">演出纪念徽章</option> <option th:selected="${badge.type == 2}" value="2">演出纪念徽章</option>
<option th:selected="${badge.type == 4}" value="4">签证页</option>
</select> </select>
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 徽章类型保存后不可修改</span> <span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 徽章类型保存后不可修改</span>
</div> </div>
...@@ -63,11 +64,11 @@ ...@@ -63,11 +64,11 @@
<textarea name="shareText" th:field="*{shareText}" class="form-control" rows="3" maxlength="255" placeholder="请输入徽章分享文案"></textarea> <textarea name="shareText" th:field="*{shareText}" class="form-control" rows="3" maxlength="255" placeholder="请输入徽章分享文案"></textarea>
</div> </div>
</div> </div>
<div class="form-group" id="ticketTimesDiv" th:style="${badge.type == 2 ? 'display:block;' : 'display:none;'}"> <div class="form-group" id="ticketTimesDiv" th:style="${badge.type == 2 or badge.type == 4 ? 'display:block;' : 'display:none;'}">
<label class="col-sm-3 control-label is-required">关联演出:</label> <label class="col-sm-3 control-label is-required">关联演出:</label>
<div class="col-sm-8"> <div class="col-sm-8">
<input name="performanceId" id="performanceId" th:field="*{performanceId}" class="form-control" type="text" placeholder="请输入演出ID" th:required="${badge.type == 2}"> <input name="performanceId" id="performanceId" th:field="*{performanceId}" class="form-control" type="text" placeholder="请输入演出ID" th:required="${badge.type == 2 or badge.type == 4}">
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 演出纪念徽章必填</span> <span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 演出纪念徽章、签证页必填</span>
</div> </div>
</div> </div>
</form> </form>
...@@ -79,7 +80,7 @@ ...@@ -79,7 +80,7 @@
var platformUrl = /*[[${platformUrl}]]*/ ''; var platformUrl = /*[[${platformUrl}]]*/ '';
function typeChange(val) { function typeChange(val) {
if (val == 2 || val == '2') { if (val == 2 || val == '2' || val == 4 || val == '4') {
$("#ticketTimesDiv").show(); $("#ticketTimesDiv").show();
$("#performanceId").prop("required", true); $("#performanceId").prop("required", true);
} else { } else {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
<option value="">所有</option> <option value="">所有</option>
<option value="1">护照纪念徽章</option> <option value="1">护照纪念徽章</option>
<option value="2">演出纪念徽章</option> <option value="2">演出纪念徽章</option>
<option value="4">签证页</option>
<!-- <option value="3">特殊徽章</option> --> <!-- <option value="3">特殊徽章</option> -->
</select> </select>
</li> </li>
...@@ -106,6 +107,7 @@ ...@@ -106,6 +107,7 @@
if (value == 1) return '<span class="badge badge-info">护照纪念</span>'; if (value == 1) return '<span class="badge badge-info">护照纪念</span>';
if (value == 2) return '<span class="badge badge-primary">演出纪念</span>'; if (value == 2) return '<span class="badge badge-primary">演出纪念</span>';
if (value == 3) return '<span class="badge badge-warning">特殊徽章</span>'; if (value == 3) return '<span class="badge badge-warning">特殊徽章</span>';
if (value == 4) return '<span class="badge badge-success">签证页</span>';
return value; return value;
} }
}, },
......
package com.liquidnet.service.adam.dto;
import lombok.Data;
@Data
public class AdamCaomeiPerformanceIdTitleDto {
private String performanceId;
private String title;
}
...@@ -44,12 +44,12 @@ public class AdamCaomeiBadge implements Serializable { ...@@ -44,12 +44,12 @@ public class AdamCaomeiBadge implements Serializable {
private String icon; private String icon;
/** /**
* 徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章 * 徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章, 4-签证页
*/ */
private Integer type; private Integer type;
/** /**
* 关联演出ID (仅演出纪念徽章必填,其他类型为空) * 关联演出ID (演出纪念徽章、签证页必填,其他类型为空)
*/ */
private String performanceId; private String performanceId;
......
...@@ -3,6 +3,7 @@ package com.liquidnet.service.adam.mapper; ...@@ -3,6 +3,7 @@ package com.liquidnet.service.adam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimCountDto; import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimCountDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimUserDto; import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimUserDto;
import com.liquidnet.service.adam.dto.AdamCaomeiPerformanceIdTitleDto;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge; import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Insert;
...@@ -107,4 +108,16 @@ public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> { ...@@ -107,4 +108,16 @@ public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> {
@Select("select title from kylin_performances where performances_id = #{performanceId}") @Select("select title from kylin_performances where performances_id = #{performanceId}")
String selectKylinPerformanceTitleById(@Param("performanceId") String performanceId); String selectKylinPerformanceTitleById(@Param("performanceId") String performanceId);
@Select({
"<script>",
"select performances_id as performanceId, title",
"from kylin_performances",
"where performances_id in",
"<foreach collection='performanceIds' item='performanceId' open='(' separator=',' close=')'>",
"#{performanceId}",
"</foreach>",
"</script>"
})
List<AdamCaomeiPerformanceIdTitleDto> selectKylinPerformanceTitlesByIds(@Param("performanceIds") List<String> performanceIds);
} }
package com.liquidnet.service.sweet.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel("草莓音乐节手册扩展配置")
public class SweetManualExtConfigDto implements Serializable {
@ApiModelProperty("电子手册id")
private String manualId;
@ApiModelProperty("餐饮攻略链接")
private String foodGuideUrl;
@ApiModelProperty("相册链接")
private String albumUrl;
@ApiModelProperty("失物招领问卷星ID")
private String lostFoundWjxId;
@ApiModelProperty("地图GeoJSON数据")
private Object mapGeojson;
}
...@@ -50,6 +50,26 @@ public class SweetManual implements Serializable,Cloneable { ...@@ -50,6 +50,26 @@ public class SweetManual implements Serializable,Cloneable {
*/ */
private Integer isReleaseManual; private Integer isReleaseManual;
/**
* 餐饮攻略链接
*/
private String foodGuideUrl;
/**
* 相册链接
*/
private String albumUrl;
/**
* 失物招领问卷星ID
*/
private String lostFoundWjxId;
/**
* 地图GeoJSON数据
*/
private String mapGeojson;
/** /**
* 创建时间 * 创建时间
*/ */
......
...@@ -3,4 +3,7 @@ package com.liquidnet.service.adam.constant; ...@@ -3,4 +3,7 @@ package com.liquidnet.service.adam.constant;
public class AdamConst { public class AdamConst {
public static final String DEF_URL_AVATAR = "https://img.zhengzai.tv/user/2021/07/27/a4cc2a4e6dcd44d1812dc60e079086b4.png"; public static final String DEF_URL_AVATAR = "https://img.zhengzai.tv/user/2021/07/27/a4cc2a4e6dcd44d1812dc60e079086b4.png";
public static final String DEF_URL_BACKGROUND = "https://img.zhengzai.tv/other/2021/07/27/150eeb0e20af4fc88e8a1ec57c46c362.png"; public static final String DEF_URL_BACKGROUND = "https://img.zhengzai.tv/other/2021/07/27/150eeb0e20af4fc88e8a1ec57c46c362.png";
/** silent_mobile_v2 静默注册来源:DouDou */
public static final String REGIST_SOURCE_DOUDOU = "doudou";
} }
...@@ -55,11 +55,11 @@ public class AdamAddressesController { ...@@ -55,11 +55,11 @@ public class AdamAddressesController {
@GetMapping("list") @GetMapping("list")
public ResponseDto<List<AdamAddressesVo>> list() { public ResponseDto<List<AdamAddressesVo>> list() {
List<AdamAddressesVo> vos = adamRdmService.getAddressesVoByUid(CurrentUtil.getCurrentUid()); List<AdamAddressesVo> vos = adamRdmService.getAddressesVoByUid(CurrentUtil.getCurrentUid());
if (!CollectionUtils.isEmpty(vos)) { /*if (!CollectionUtils.isEmpty(vos)) {
for (AdamAddressesVo vo : vos) { for (AdamAddressesVo vo : vos) {
vo.setPhone(SensitizeUtil.custom(vo.getPhone(), 3, 4)); vo.setPhone(SensitizeUtil.custom(vo.getPhone(), 3, 4));
} }
} }*/
return ResponseDto.success(vos); return ResponseDto.success(vos);
} }
......
...@@ -36,7 +36,7 @@ public class AdamCaomeiPassportUserController { ...@@ -36,7 +36,7 @@ public class AdamCaomeiPassportUserController {
} }
@ApiOperationSupport(order = 2) @ApiOperationSupport(order = 2)
@ApiOperation("护照首页聚合数据") @ApiOperation("护照首页聚合数据(含签证页 type=4 字段 visaBadges,访问时自动静默发放)")
@GetMapping("home") @GetMapping("home")
public ResponseDto<AdamCaomeiPassportHomeVo> home() { public ResponseDto<AdamCaomeiPassportHomeVo> home() {
return adamCaomeiPassportUserService.getPassportHome(); return adamCaomeiPassportUserService.getPassportHome();
......
...@@ -13,6 +13,7 @@ import com.liquidnet.common.sms.processor.SmsProcessor; ...@@ -13,6 +13,7 @@ import com.liquidnet.common.sms.processor.SmsProcessor;
import com.liquidnet.commons.lang.constant.LnsEnum; import com.liquidnet.commons.lang.constant.LnsEnum;
import com.liquidnet.commons.lang.core.JwtValidator; import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.constant.AdamConst;
import com.liquidnet.service.adam.dto.AdamThirdPartParam; import com.liquidnet.service.adam.dto.AdamThirdPartParam;
import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo; import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo; import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
...@@ -351,7 +352,7 @@ public class AdamLoginController { ...@@ -351,7 +352,7 @@ public class AdamLoginController {
log.error("login by silent for mobile:{},{}/{},{}-{}", mobile, otp, otpDecrypt, l, reql); log.error("login by silent for mobile:{},{}/{},{}-{}", mobile, otp, otpDecrypt, l, reql);
return ResponseDto.failure(ErrorMapping.get("10005")); return ResponseDto.failure(ErrorMapping.get("10005"));
} }
return this.silentMobileLoginSuccess(mobile); return this.silentMobileLoginSuccess(mobile, null);
} }
@ApiOperationSupport(order = 7) @ApiOperationSupport(order = 7)
...@@ -371,14 +372,16 @@ public class AdamLoginController { ...@@ -371,14 +372,16 @@ public class AdamLoginController {
log.error("login by silent v2 otp invalid, mobile:{}", mobile); log.error("login by silent v2 otp invalid, mobile:{}", mobile);
return ResponseDto.failure(ErrorMapping.get("10005")); return ResponseDto.failure(ErrorMapping.get("10005"));
} }
return this.silentMobileLoginSuccess(mobile); return this.silentMobileLoginSuccess(mobile, AdamConst.REGIST_SOURCE_DOUDOU);
} }
private ResponseDto<AdamLoginInfoVo> silentMobileLoginSuccess(String mobile) { private ResponseDto<AdamLoginInfoVo> silentMobileLoginSuccess(String mobile, String registSource) {
String uid = adamRdmService.getUidByMobile(mobile); String uid = adamRdmService.getUidByMobile(mobile);
boolean toRegister = StringUtils.isEmpty(uid); boolean toRegister = StringUtils.isEmpty(uid);
AdamUserInfoVo userInfoVo = toRegister ? adamUserService.register(mobile) : adamRdmService.getUserInfoVoByUid(uid); AdamUserInfoVo userInfoVo = toRegister
? adamUserService.register(mobile, 0, registSource)
: adamRdmService.getUserInfoVoByUid(uid);
if (!toRegister && (null == userInfoVo || userInfoVo.getState() == 2)) { if (!toRegister && (null == userInfoVo || userInfoVo.getState() == 2)) {
log.warn("Cancelled mobile:{}", mobile); log.warn("Cancelled mobile:{}", mobile);
......
...@@ -12,6 +12,7 @@ import com.liquidnet.commons.lang.util.JsonUtils; ...@@ -12,6 +12,7 @@ import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.SensitizeUtil; import com.liquidnet.commons.lang.util.SensitizeUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst; import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto; import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.AdamCaomeiPerformanceIdTitleDto;
import com.liquidnet.service.adam.dto.AdamUserInfoDto; import com.liquidnet.service.adam.dto.AdamUserInfoDto;
import com.liquidnet.service.adam.dto.vo.*; import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge; import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
...@@ -807,21 +808,62 @@ public class AdamRdmService { ...@@ -807,21 +808,62 @@ public class AdamRdmService {
* 读取不到时返回 null。 * 读取不到时返回 null。
*/ */
public String getPerformanceTitleById(String performanceId) { public String getPerformanceTitleById(String performanceId) {
if (StringUtils.isEmpty(performanceId)) {
return null;
}
String id = performanceId.trim();
Map<String, String> map = getPerformanceTitleMapByIds(Collections.singletonList(id));
return map.get(id);
}
/**
* 批量解析演出 ID → 标题:先走 Kylin Redis,缓存未命中的 ID 合并为一次 DB IN 查询。
*/
public Map<String, String> getPerformanceTitleMapByIds(Collection<String> performanceIds) {
if (CollectionUtils.isEmpty(performanceIds)) {
return Collections.emptyMap();
}
List<String> ids = performanceIds.stream()
.filter(id -> !StringUtils.isEmpty(id))
.map(String::trim)
.distinct()
.collect(Collectors.toList());
if (ids.isEmpty()) {
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>(ids.size() * 2);
List<String> missingFromCache = new ArrayList<>();
for (String id : ids) {
try { try {
KylinPerformanceVo vo = getKylinPerformanceVoById(performanceId); KylinPerformanceVo vo = getKylinPerformanceVoById(id);
if (vo != null) { if (vo != null) {
return vo.getTitle(); String title = vo.getTitle();
}
// 从数据库查询
String title = adamCaomeiBadgeMapper.selectKylinPerformanceTitleById(performanceId);
if (!StringUtils.isEmpty(title)) { if (!StringUtils.isEmpty(title)) {
return title; result.put(id, title);
}
} else {
missingFromCache.add(id);
} }
return null;
} catch (Exception e) { } catch (Exception e) {
log.warn("[getPerformanceTitleById] 读取演出缓存失败, performanceId: {}", performanceId, e); log.warn("[getPerformanceTitleMapByIds] 读取演出缓存失败, performanceId: {}", id, e);
return null; missingFromCache.add(id);
}
}
if (!missingFromCache.isEmpty()) {
List<AdamCaomeiPerformanceIdTitleDto> rows =
adamCaomeiBadgeMapper.selectKylinPerformanceTitlesByIds(missingFromCache);
if (!CollectionUtils.isEmpty(rows)) {
for (AdamCaomeiPerformanceIdTitleDto row : rows) {
if (row == null || StringUtils.isEmpty(row.getPerformanceId()) || StringUtils.isEmpty(row.getTitle())) {
continue;
}
result.put(row.getPerformanceId().trim(), row.getTitle());
}
}
} }
return result;
} }
/** /**
......
package com.liquidnet.service.adam.service.caomei;
import java.util.Collections;
import java.util.Set;
/**
* 用户补签审核状态快照(按「徽章 ID」与「场次 performanceId」两个维度聚合)。
* <p>
* 由 {@link CaomeiBadgeEligibilityService#loadApplyBadgeStatus} 从 DB 补签表加载,
* 供护照首页货架({@code claimable} / {@code applyPending})与演出徽章认领前置判断共用,
* 避免 home 展示与 POST claim 规则不一致。
*/
public final class CaomeiBadgeApplyStatus {
/** 审核已通过(auditStatus=1)的补签申请对应的 badgeId */
private final Set<String> passedApplyBadgeIds;
/** 待审核(auditStatus=0)的补签申请对应的 badgeId */
private final Set<String> pendingApplyBadgeIds;
/** 审核已通过补签所关联的场次 ID(含从 badge 配置兜底解析的 performanceId) */
private final Set<String> passedApplyPerformanceIds;
/** 待审核补签所关联的场次 ID */
private final Set<String> pendingApplyPerformanceIds;
/**
* @param passedApplyBadgeIds 已通过补签的徽章 ID 集合,可为 null(视为空集)
* @param pendingApplyBadgeIds 待审核补签的徽章 ID 集合,可为 null
* @param passedApplyPerformanceIds 已通过补签的场次 ID 集合,可为 null
* @param pendingApplyPerformanceIds 待审核补签的场次 ID 集合,可为 null
*/
public CaomeiBadgeApplyStatus(Set<String> passedApplyBadgeIds,
Set<String> pendingApplyBadgeIds,
Set<String> passedApplyPerformanceIds,
Set<String> pendingApplyPerformanceIds) {
this.passedApplyBadgeIds = passedApplyBadgeIds == null ? Collections.emptySet() : passedApplyBadgeIds;
this.pendingApplyBadgeIds = pendingApplyBadgeIds == null ? Collections.emptySet() : pendingApplyBadgeIds;
this.passedApplyPerformanceIds = passedApplyPerformanceIds == null ? Collections.emptySet() : passedApplyPerformanceIds;
this.pendingApplyPerformanceIds = pendingApplyPerformanceIds == null ? Collections.emptySet() : pendingApplyPerformanceIds;
}
public Set<String> getPassedApplyBadgeIds() {
return passedApplyBadgeIds;
}
public Set<String> getPendingApplyBadgeIds() {
return pendingApplyBadgeIds;
}
public Set<String> getPassedApplyPerformanceIds() {
return passedApplyPerformanceIds;
}
public Set<String> getPendingApplyPerformanceIds() {
return pendingApplyPerformanceIds;
}
}
package com.liquidnet.service.adam.service.caomei;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.util.QueueUtils;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
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.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 草莓徽章「发放」统一入口:写入用户徽章 Redis({@code INFO_CAOMEI_BADGE_USER + uid}),
* 并通过 MQ 异步 INSERT {@code adam_caomei_user_badge}。
* <p>
* 调用方:绑护照发 type=1、用户 claim、home 静默发 type=4 签证页。
* <p>
* {@code source} 与库表一致:1-绑定护照,2-购票自动,3-补签通过,4-现场管理员(本服务未使用 4)。
*/
@Slf4j
@Service
public class CaomeiBadgeGrantService {
@Autowired
private AdamRdmService adamRdmService;
@Autowired
private QueueUtils queueUtils;
/**
* 将运营配置的徽章实体转为用户已获列表中的缓存 DTO(尚未写 Redis)。
*
* @param badge 上架徽章配置(含 badgeId、名称、type、performanceId 等)
* @param source 获得途径,见类说明
* @param claimedAt 获得时间,null 时使用当前时间
*/
public AdamCaomeiPassportUserBadgeDto toUserBadgeDto(AdamCaomeiBadge badge, int source, Date claimedAt) {
AdamCaomeiPassportUserBadgeDto dto = new AdamCaomeiPassportUserBadgeDto();
dto.setBadgeId(badge.getBadgeId());
dto.setBadgeName(StringUtils.defaultString(badge.getName()));
dto.setSubTitle(StringUtils.defaultString(badge.getSubTitle()));
dto.setIcon(StringUtils.defaultString(badge.getIcon()));
dto.setShareText(StringUtils.defaultString(badge.getShareText()));
dto.setType(badge.getType());
dto.setPerformanceId(StringUtils.trimToEmpty(badge.getPerformanceId()));
dto.setClaimedAt(claimedAt != null ? claimedAt : DateUtil.now());
dto.setSource(source);
return dto;
}
/**
* 批量发放:对比用户当前已拥有 badgeId,仅对「尚未拥有」的徽章追加缓存并落库。
* <p>
* 适用于:绑护照一次性发多个 type=1、home 末尾静默发多个 type=4。
*
* @param uid 当前用户 ID
* @param badges 待发放的配置列表(通常来自 published 缓存筛选)
* @param source 获得途径
* @param grantAt 统一获得时间(批量场景 bind/签证 使用同一时间戳),null 则取当前时间
* @return 本次实际写入 Redis 的 DTO;空列表表示全部已拥有、未发生发放
*/
public List<AdamCaomeiPassportUserBadgeDto> grantBadgesIfAbsent(String uid,
List<AdamCaomeiBadge> badges,
int source,
Date grantAt) {
if (StringUtils.isBlank(uid) || CollectionUtils.isEmpty(badges)) {
return new ArrayList<>();
}
Date at = grantAt != null ? grantAt : DateUtil.now();
List<AdamCaomeiPassportUserBadgeDto> cacheList = copyUserBadgeCache(uid);
Set<String> existedBadgeIds = cacheList.stream()
.map(AdamCaomeiPassportUserBadgeDto::getBadgeId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
List<AdamCaomeiPassportUserBadgeDto> appendVos = badges.stream()
.filter(b -> b != null && StringUtils.isNotBlank(b.getBadgeId()))
.filter(b -> !existedBadgeIds.contains(b.getBadgeId()))
.map(b -> toUserBadgeDto(b, source, at))
.collect(Collectors.toList());
if (appendVos.isEmpty()) {
return appendVos;
}
adamRdmService.addUserCaomeiBadgeDtosByUid(uid, cacheList, appendVos);
sendUserBadgeInsertMq(uid, appendVos, source, at);
return appendVos;
}
/**
* 单条发放:用于 POST claim 循环内逐枚写入。
* <p>
* 调用方须已做「未领取」校验;本方法不再查重。
*
* @param uid 用户 ID
* @param badge 单枚上架徽章配置
* @param source 获得途径(type=2 时由 Eligibility 解析为 2 或 3)
* @param mutableCacheList 当前请求内持有的用户徽章列表副本,发放后会 append 并写回 Redis
*/
public void grantOne(String uid,
AdamCaomeiBadge badge,
int source,
List<AdamCaomeiPassportUserBadgeDto> mutableCacheList) {
if (badge == null || StringUtils.isBlank(badge.getBadgeId())) {
return;
}
Date now = new Date();
AdamCaomeiPassportUserBadgeDto dto = toUserBadgeDto(badge, source, now);
adamRdmService.addUserCaomeiBadgeDtoByUid(uid, mutableCacheList, dto);
queueUtils.sendMsgByRedis(
MQConst.AdamQueue.SQL_UCENTER.getKey(),
SqlMapping.get("adam_caomei_user_badge.add", uid, badge.getBadgeId(), source, now)
);
}
/** 读取用户徽章 Redis,返回可修改副本(miss 时可能为空列表) */
private List<AdamCaomeiPassportUserBadgeDto> copyUserBadgeCache(String uid) {
List<AdamCaomeiPassportUserBadgeDto> cache = adamRdmService.getUserCaomeiBadgesByUid(uid);
if (cache == null) {
return new ArrayList<>();
}
return new ArrayList<>(cache);
}
/** 批量 MQ:仅对 appendVos 中的 badgeId 发送 INSERT,与 Redis 追加范围一致 */
private void sendUserBadgeInsertMq(String uid,
List<AdamCaomeiPassportUserBadgeDto> appendVos,
int source,
Date grantAt) {
LinkedList<Object[]> paramsList = new LinkedList<>();
for (AdamCaomeiPassportUserBadgeDto dto : appendVos) {
if (dto == null || StringUtils.isBlank(dto.getBadgeId())) {
continue;
}
paramsList.add(new Object[]{uid, dto.getBadgeId(), source, grantAt});
}
if (paramsList.isEmpty()) {
return;
}
queueUtils.sendMsgByRedis(
MQConst.AdamQueue.SQL_UCENTER.getKey(),
SqlMapping.get("adam_caomei_user_badge.add", paramsList)
);
}
}
...@@ -7,7 +7,6 @@ import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto; ...@@ -7,7 +7,6 @@ import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyParam; import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyParam;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyRecordUserDto; import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyRecordUserDto;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyRecordUserVo; import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyRecordUserVo;
import com.liquidnet.service.adam.dto.vo.AdamRealInfoVo;
import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord; import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge; import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.entity.AdamCaomeiPassport; import com.liquidnet.service.adam.entity.AdamCaomeiPassport;
...@@ -16,6 +15,8 @@ import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper; ...@@ -16,6 +15,8 @@ import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper; import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.service.AdamRdmService; import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamCaomeiBadgeUserService; import com.liquidnet.service.adam.service.IAdamCaomeiBadgeUserService;
import com.liquidnet.service.adam.service.caomei.CaomeiBadgeEligibilityService;
import com.liquidnet.service.adam.service.caomei.CaomeiBadgeGrantService;
import com.liquidnet.service.adam.util.QueueUtils; import com.liquidnet.service.adam.util.QueueUtils;
import com.liquidnet.service.base.ErrorMapping; import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
...@@ -29,12 +30,10 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -29,12 +30,10 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.HashSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
...@@ -50,6 +49,10 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi ...@@ -50,6 +49,10 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
@Autowired @Autowired
private AdamRdmService adamRdmService; private AdamRdmService adamRdmService;
@Autowired @Autowired
private CaomeiBadgeGrantService caomeiBadgeGrantService;
@Autowired
private CaomeiBadgeEligibilityService caomeiBadgeEligibilityService;
@Autowired
private QueueUtils queueUtils; private QueueUtils queueUtils;
@Override @Override
...@@ -133,56 +136,13 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi ...@@ -133,56 +136,13 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
} }
} }
Set<String> paidPerformanceSet = Collections.emptySet(); CaomeiBadgeEligibilityService.Type2ClaimContext type2Ctx = null;
Set<String> passedApplyBadgeIds = Collections.emptySet();
Set<String> passedApplyPerformanceIds = Collections.emptySet();
Map<String, Set<String>> perfAllBadgeIds = Collections.emptyMap();
if (hasType2) { if (hasType2) {
AdamRealInfoVo real = adamRdmService.getRealInfoVoByUidPlain(uid); type2Ctx = caomeiBadgeEligibilityService.buildType2ClaimContext(uid, orderedBadges);
if (real == null || real.getState() == null || real.getState() != 1 || StringUtils.isBlank(real.getIdCard())) { if (!type2Ctx.isRealNameOk()) {
log.error("[claimBadges] 认领演出徽章需先实名, uid: {}, badgeIds: {}", uid, requestBadgeIds); log.error("[claimBadges] 认领演出徽章需先实名, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10610")); return ResponseDto.failure(ErrorMapping.get("10610"));
} }
List<String> paidPerformanceIds = adamRdmService.getPaidPerformanceIdsByIdCard(real.getIdCard());
paidPerformanceSet = paidPerformanceIds == null ? Collections.emptySet() : new HashSet<>(paidPerformanceIds);
List<AdamCaomeiBadgeApplyRecord> passedApplyRecords = badgeApplyRecordMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
.eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
.eq(AdamCaomeiBadgeApplyRecord::getAuditStatus, 1)
);
passedApplyBadgeIds = new HashSet<>();
passedApplyPerformanceIds = new HashSet<>();
if (passedApplyRecords != null) {
for (AdamCaomeiBadgeApplyRecord r : passedApplyRecords) {
if (r == null) {
continue;
}
if (StringUtils.isNotBlank(r.getBadgeId())) {
passedApplyBadgeIds.add(r.getBadgeId());
}
if (StringUtils.isNotBlank(r.getPerformanceId())) {
passedApplyPerformanceIds.add(r.getPerformanceId());
}
}
}
Set<String> targetPerfIds = orderedBadges.stream()
.filter(b -> b.getType() != null && b.getType() == 2)
.map(AdamCaomeiBadge::getPerformanceId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (!targetPerfIds.isEmpty()) {
List<AdamCaomeiBadge> perfBadges = adamCaomeiBadgeMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadge.class)
.eq(AdamCaomeiBadge::getType, 2)
.in(AdamCaomeiBadge::getPerformanceId, targetPerfIds)
);
perfAllBadgeIds = perfBadges.stream()
.filter(b -> StringUtils.isNotBlank(b.getPerformanceId()) && StringUtils.isNotBlank(b.getBadgeId()))
.collect(Collectors.groupingBy(AdamCaomeiBadge::getPerformanceId,
Collectors.mapping(AdamCaomeiBadge::getBadgeId, Collectors.toSet())));
}
} }
List<String> claimedBadgeIds = new ArrayList<>(); List<String> claimedBadgeIds = new ArrayList<>();
...@@ -190,25 +150,14 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi ...@@ -190,25 +150,14 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
int type = badge.getType() == null ? 0 : badge.getType(); int type = badge.getType() == null ? 0 : badge.getType();
int source = 1; int source = 1;
if (type == 2) { if (type == 2) {
String perfId = StringUtils.trimToEmpty(badge.getPerformanceId()); if (!caomeiBadgeEligibilityService.canClaimPerformanceBadge(badge, type2Ctx)) {
boolean hasPaidRecord = StringUtils.isNotBlank(perfId) && paidPerformanceSet.contains(perfId);
boolean hasPassedApply = false;
if (StringUtils.isNotBlank(perfId)) {
if (passedApplyPerformanceIds.contains(perfId)) {
hasPassedApply = true;
} else {
Set<String> badgeIdSet = perfAllBadgeIds.getOrDefault(perfId, Collections.emptySet());
hasPassedApply = badgeIdSet.stream().anyMatch(passedApplyBadgeIds::contains);
}
}
if (!hasPaidRecord && !hasPassedApply) {
log.error("[claimBadges] 无购票记录且无通过补签,无法认领, uid: {}, badgeId: {}", uid, badge.getBadgeId()); log.error("[claimBadges] 无购票记录且无通过补签,无法认领, uid: {}, badgeId: {}", uid, badge.getBadgeId());
return ResponseDto.failure(ErrorMapping.get("10611")); return ResponseDto.failure(ErrorMapping.get("10611"));
} }
source = hasPaidRecord ? 2 : 3; source = caomeiBadgeEligibilityService.resolveClaimSourceForType2(badge, type2Ctx);
} }
grantUserBadgeRedisThenMq(uid, badge, source, badgeVos); caomeiBadgeGrantService.grantOne(uid, badge, source, badgeVos);
claimedBadgeIds.add(badge.getBadgeId()); claimedBadgeIds.add(badge.getBadgeId());
} }
...@@ -216,33 +165,6 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi ...@@ -216,33 +165,6 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
return ResponseDto.success(claimedBadgeIds); return ResponseDto.success(claimedBadgeIds);
} }
/**
* Redis 追加用户徽章展示 DTO,再发 MQ 异步执行 sqlmap 中的 INSERT。
*/
private void grantUserBadgeRedisThenMq(String uid, AdamCaomeiBadge badge, int source,
List<AdamCaomeiPassportUserBadgeDto> badgeVos) {
Date now = new Date();
AdamCaomeiPassportUserBadgeDto dto = new AdamCaomeiPassportUserBadgeDto();
dto.setBadgeId(badge.getBadgeId());
dto.setBadgeName(StringUtils.defaultString(badge.getName()));
dto.setIcon(StringUtils.defaultString(badge.getIcon()));
dto.setShareText(StringUtils.defaultString(badge.getShareText()));
dto.setType(badge.getType());
dto.setClaimedAt(now);
dto.setSource(source);
dto.setSubTitle(badge.getSubTitle());
dto.setPerformanceId(StringUtils.defaultString(badge.getPerformanceId()));
adamRdmService.addUserCaomeiBadgeDtoByUid(uid, badgeVos, dto);
long t = System.currentTimeMillis();
queueUtils.sendMsgByRedis(
MQConst.AdamQueue.SQL_UCENTER.getKey(),
SqlMapping.get("adam_caomei_user_badge.add", uid, badge.getBadgeId(), source, now)
);
log.debug("[claimBadge] MQ耗时:{}ms, uid: {}, badgeId: {}", System.currentTimeMillis() - t, uid, badge.getBadgeId());
}
@Override @Override
public ResponseDto<List<AdamCaomeiBadgeApplyRecordUserVo>> getApplyRecords(String uid) { public ResponseDto<List<AdamCaomeiBadgeApplyRecordUserVo>> getApplyRecords(String uid) {
if (StringUtils.isBlank(uid)) { if (StringUtils.isBlank(uid)) {
......
...@@ -61,6 +61,11 @@ public class AdamUserServiceImpl implements IAdamUserService { ...@@ -61,6 +61,11 @@ public class AdamUserServiceImpl implements IAdamUserService {
@Override @Override
public AdamUserInfoVo register(String mobile, int isComplete) { public AdamUserInfoVo register(String mobile, int isComplete) {
return this.register(mobile, isComplete, null);
}
@Override
public AdamUserInfoVo register(String mobile, int isComplete, String registSource) {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
AdamUserInfoVo userInfoVo = AdamUserInfoVo.getNew(); AdamUserInfoVo userInfoVo = AdamUserInfoVo.getNew();
...@@ -82,12 +87,12 @@ public class AdamUserServiceImpl implements IAdamUserService { ...@@ -82,12 +87,12 @@ public class AdamUserServiceImpl implements IAdamUserService {
String[] mobileLocateArr = adamRdmService.getMobileLocateArr(mobile); String[] mobileLocateArr = adamRdmService.getMobileLocateArr(mobile);
toMqSqls.add(SqlMapping.get("adam_user_mobile_locate.add")); toMqSqls.add(SqlMapping.get("adam_user_mobile_locate.add"));
String cliIpAddr = CurrentUtil.getCliIpAddr(); String cliIpAddr = CurrentUtil.getCliIpAddr();
String headerCliSource = CurrentUtil.getHeaderCliSource(); String cliSource = StringUtils.isNotBlank(registSource) ? registSource : CurrentUtil.getHeaderCliSource();
if (null != mobileLocateArr && mobileLocateArr.length > 0) { if (null != mobileLocateArr && mobileLocateArr.length > 0) {
initUserMobileLocateObjs.add(new Object[]{ initUserMobileLocateObjs.add(new Object[]{
userInfoVo.getUid(), mobile, 1, userInfoVo.getUid(), mobile, 1,
mobileLocateArr[0], mobileLocateArr[1], mobileLocateArr[2], mobileLocateArr[3], mobileLocateArr[4], mobileLocateArr[0], mobileLocateArr[1], mobileLocateArr[2], mobileLocateArr[3], mobileLocateArr[4],
mobile, cliIpAddr, headerCliSource, now, cliIpAddr, headerCliSource, now, now mobile, cliIpAddr, cliSource, now, cliIpAddr, cliSource, now, now
}); });
userInfoVo.setProvince(mobileLocateArr[0]); userInfoVo.setProvince(mobileLocateArr[0]);
userInfoVo.setCity(mobileLocateArr[1]); userInfoVo.setCity(mobileLocateArr[1]);
...@@ -95,7 +100,7 @@ public class AdamUserServiceImpl implements IAdamUserService { ...@@ -95,7 +100,7 @@ public class AdamUserServiceImpl implements IAdamUserService {
initUserMobileLocateObjs.add(new Object[]{ initUserMobileLocateObjs.add(new Object[]{
userInfoVo.getUid(), mobile, 1, userInfoVo.getUid(), mobile, 1,
null, null, null, null, null, null, null, null, null, null,
mobile, cliIpAddr, headerCliSource, now, cliIpAddr, headerCliSource, now, now mobile, cliIpAddr, cliSource, now, cliIpAddr, cliSource, now, now
}); });
} }
......
use ln_scene;
ALTER TABLE `sweet_manual`
ADD COLUMN `food_guide_url` varchar(500) NOT NULL DEFAULT '' COMMENT '餐饮攻略链接' AFTER `is_release_manual`,
ADD COLUMN `album_url` varchar(500) NOT NULL DEFAULT '' COMMENT '相册链接' AFTER `food_guide_url`,
ADD COLUMN `lost_found_wjx_id` varchar(200) NOT NULL DEFAULT '' COMMENT '失物招领问卷星ID' AFTER `album_url`,
ADD COLUMN `map_geojson` longtext NULL COMMENT '地图GeoJSON数据' AFTER `lost_found_wjx_id`;
use ln_scene;
-- 地图GeoJSON由文件地址改为直接存储JSON数据
ALTER TABLE `sweet_manual`
CHANGE COLUMN `map_geojson_url` `map_geojson` longtext NULL COMMENT '地图GeoJSON数据';
...@@ -5,6 +5,7 @@ import com.liquidnet.service.base.ResponseDto; ...@@ -5,6 +5,7 @@ import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.dto.SweetManualAppletDto; import com.liquidnet.service.sweet.dto.SweetManualAppletDto;
import com.liquidnet.service.sweet.dto.SweetManualArtistList2Dto; import com.liquidnet.service.sweet.dto.SweetManualArtistList2Dto;
import com.liquidnet.service.sweet.dto.SweetManualArtistListDto; import com.liquidnet.service.sweet.dto.SweetManualArtistListDto;
import com.liquidnet.service.sweet.dto.SweetManualExtConfigDto;
import com.liquidnet.service.sweet.dto.SweetPerformArtistTimeListDto; import com.liquidnet.service.sweet.dto.SweetPerformArtistTimeListDto;
import com.liquidnet.service.sweet.entity.SweetManualNotify; import com.liquidnet.service.sweet.entity.SweetManualNotify;
import com.liquidnet.service.sweet.entity.SweetManualShop; import com.liquidnet.service.sweet.entity.SweetManualShop;
...@@ -228,6 +229,15 @@ public class SweetAppletController { ...@@ -228,6 +229,15 @@ public class SweetAppletController {
return ResponseDto.success(redisDataUtils.getRichTextRedisData(manualId, type)); return ResponseDto.success(redisDataUtils.getRichTextRedisData(manualId, type));
} }
@GetMapping("manualExt")
@ApiOperation("草莓音乐节手册扩展配置")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true),
})
public ResponseDto<SweetManualExtConfigDto> manualExt(@RequestParam String manualId) {
return ResponseDto.success(redisDataUtils.getManualExtConfigRedisData(manualId));
}
@PostMapping("artistsWatch") @PostMapping("artistsWatch")
@ApiOperation("艺人-想看") @ApiOperation("艺人-想看")
@ApiImplicitParams({ @ApiImplicitParams({
......
package com.liquidnet.service.sweet.controller;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.dto.SweetManualExtConfigDto;
import com.liquidnet.service.sweet.param.SweetManualExtConfigParam;
import com.liquidnet.service.sweet.param.SweetManualMapGeojsonParam;
import com.liquidnet.service.sweet.service.ISweetManualExtConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Api(tags = "草莓音乐节手册扩展配置")
@RestController
@RequestMapping("/sweet-manual-ext")
public class SweetManualExtConfigController {
@Autowired
private ISweetManualExtConfigService sweetManualExtConfigService;
@GetMapping("get")
@ApiOperation("获取手册扩展配置")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true),
})
public ResponseDto<SweetManualExtConfigDto> get(@RequestParam String manualId) {
return sweetManualExtConfigService.get(manualId);
}
@PostMapping("save")
@ApiOperation("保存手册扩展配置")
public ResponseDto<Boolean> save(@RequestBody SweetManualExtConfigParam param) {
return sweetManualExtConfigService.save(param);
}
@GetMapping("mapGeojson")
@ApiOperation("获取地图GeoJSON数据")
@ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true),
})
public ResponseDto<Object> getMapGeojson(@RequestParam String manualId) {
return sweetManualExtConfigService.getMapGeojson(manualId);
}
@PostMapping("saveMapGeojson")
@ApiOperation("保存地图GeoJSON数据")
public ResponseDto<Boolean> saveMapGeojson(@RequestBody SweetManualMapGeojsonParam param) {
return sweetManualExtConfigService.saveMapGeojson(param);
}
}
...@@ -2,8 +2,9 @@ package com.liquidnet.service.sweet.controller; ...@@ -2,8 +2,9 @@ package com.liquidnet.service.sweet.controller;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.entity.SweetManualSort;
import com.liquidnet.service.sweet.service.ISweetManualSortService; import com.liquidnet.service.sweet.service.ISweetManualSortService;
import com.liquidnet.service.sweet.vo.SweetManualSortOptionVo;
import com.liquidnet.service.sweet.vo.SweetManualSortVo;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
...@@ -11,6 +12,8 @@ import io.swagger.annotations.ApiOperation; ...@@ -11,6 +12,8 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
/** /**
* <p> * <p>
* 电子宣传手册显示内容表 前端控制器 * 电子宣传手册显示内容表 前端控制器
...@@ -29,22 +32,30 @@ public class SweetManualSortController { ...@@ -29,22 +32,30 @@ public class SweetManualSortController {
@PostMapping("add") @PostMapping("add")
@ApiOperation("操作 电子手册tag") @ApiOperation("保存电子手册显示模块")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true), @ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true),
@ApiImplicitParam(type = "query", dataType = "String", name = "content", value = "内容 例子(POSITION_1,POSITION_2)", required = true) @ApiImplicitParam(type = "query", dataType = "String", name = "content",
value = "模块标识,逗号分隔。可选: artist,signingTime,siteMap,howToReach,officialSupport,audienceNotice,preventionGuidelines,notice,strategy,customerService,foodGuide,album,lostFound,mapGeojson",
required = true)
}) })
public ResponseDto<Boolean> changeStatus(@RequestParam() String manualId, public ResponseDto<Boolean> save(@RequestParam String manualId,
@RequestParam() String content) { @RequestParam String content) {
return sweetManualSortService.add(manualId, content); return sweetManualSortService.add(manualId, content);
} }
@GetMapping("get") @GetMapping("get")
@ApiOperation("获取 电子手册tag") @ApiOperation("获取电子手册显示模块")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true) @ApiImplicitParam(type = "query", dataType = "String", name = "manualId", value = "电子手册id", required = true)
}) })
public ResponseDto<SweetManualSort> changeStatus(@RequestParam() String manualId) { public ResponseDto<SweetManualSortVo> get(@RequestParam String manualId) {
return sweetManualSortService.get(manualId); return sweetManualSortService.get(manualId);
} }
@GetMapping("options")
@ApiOperation("获取可选显示模块列表")
public ResponseDto<List<SweetManualSortOptionVo>> options() {
return sweetManualSortService.options();
}
} }
package com.liquidnet.service.sweet.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.dto.SweetManualExtConfigDto;
import com.liquidnet.service.sweet.entity.SweetManual;
import com.liquidnet.service.sweet.mapper.SweetManualMapper;
import com.liquidnet.service.sweet.param.SweetManualExtConfigParam;
import com.liquidnet.service.sweet.param.SweetManualMapGeojsonParam;
import com.liquidnet.service.sweet.service.ISweetManualExtConfigService;
import com.liquidnet.service.sweet.utils.RedisDataUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class SweetManualExtConfigServiceImpl implements ISweetManualExtConfigService {
@Autowired
private SweetManualMapper sweetManualMapper;
@Autowired
private RedisDataUtils redisDataUtils;
@Override
public ResponseDto<SweetManualExtConfigDto> get(String manualId) {
SweetManual manual = sweetManualMapper.selectOne(
Wrappers.lambdaQuery(SweetManual.class).eq(SweetManual::getManualId, manualId));
if (manual == null) {
return ResponseDto.failure("手册不存在");
}
return ResponseDto.success(toDto(manual));
}
@Override
public ResponseDto<Boolean> save(SweetManualExtConfigParam param) {
if (StringUtil.isBlank(param.getManualId())) {
return ResponseDto.failure("手册id不能为空");
}
SweetManual exist = sweetManualMapper.selectOne(
Wrappers.lambdaQuery(SweetManual.class).eq(SweetManual::getManualId, param.getManualId()));
if (exist == null) {
return ResponseDto.failure("手册不存在");
}
SweetManual update = SweetManual.getNew();
update.setFoodGuideUrl(nullToEmpty(param.getFoodGuideUrl()));
update.setAlbumUrl(nullToEmpty(param.getAlbumUrl()));
update.setLostFoundWjxId(nullToEmpty(param.getLostFoundWjxId()));
if (param.getMapGeojson() != null) {
update.setMapGeojson(serializeGeojson(param.getMapGeojson()));
}
update.setUpdatedAt(LocalDateTime.now());
sweetManualMapper.update(update, Wrappers.lambdaUpdate(SweetManual.class)
.eq(SweetManual::getManualId, param.getManualId()));
redisDataUtils.setManualExtConfigRedisData(param.getManualId(), toDto(exist, param));
return ResponseDto.success(true);
}
@Override
public ResponseDto<Object> getMapGeojson(String manualId) {
SweetManual manual = sweetManualMapper.selectOne(
Wrappers.lambdaQuery(SweetManual.class).eq(SweetManual::getManualId, manualId));
if (manual == null) {
return ResponseDto.failure("手册不存在");
}
return ResponseDto.success(parseGeojson(manual.getMapGeojson()));
}
@Override
public ResponseDto<Boolean> saveMapGeojson(SweetManualMapGeojsonParam param) {
if (StringUtil.isBlank(param.getManualId())) {
return ResponseDto.failure("手册id不能为空");
}
if (param.getMapGeojson() == null) {
return ResponseDto.failure("地图GeoJSON数据不能为空");
}
SweetManual exist = sweetManualMapper.selectOne(
Wrappers.lambdaQuery(SweetManual.class).eq(SweetManual::getManualId, param.getManualId()));
if (exist == null) {
return ResponseDto.failure("手册不存在");
}
String geojson = serializeGeojson(param.getMapGeojson());
SweetManual update = SweetManual.getNew();
update.setMapGeojson(geojson);
update.setUpdatedAt(LocalDateTime.now());
sweetManualMapper.update(update, Wrappers.lambdaUpdate(SweetManual.class)
.eq(SweetManual::getManualId, param.getManualId()));
SweetManualExtConfigDto cacheDto = toDto(exist);
cacheDto.setMapGeojson(parseGeojson(geojson));
redisDataUtils.setManualExtConfigRedisData(param.getManualId(), cacheDto);
return ResponseDto.success(true);
}
private SweetManualExtConfigDto toDto(SweetManual manual) {
SweetManualExtConfigDto dto = new SweetManualExtConfigDto();
dto.setManualId(manual.getManualId());
dto.setFoodGuideUrl(manual.getFoodGuideUrl());
dto.setAlbumUrl(manual.getAlbumUrl());
dto.setLostFoundWjxId(manual.getLostFoundWjxId());
dto.setMapGeojson(parseGeojson(manual.getMapGeojson()));
return dto;
}
private SweetManualExtConfigDto toDto(SweetManual exist, SweetManualExtConfigParam param) {
SweetManualExtConfigDto dto = new SweetManualExtConfigDto();
dto.setManualId(param.getManualId());
dto.setFoodGuideUrl(nullToEmpty(param.getFoodGuideUrl()));
dto.setAlbumUrl(nullToEmpty(param.getAlbumUrl()));
dto.setLostFoundWjxId(nullToEmpty(param.getLostFoundWjxId()));
if (param.getMapGeojson() != null) {
dto.setMapGeojson(param.getMapGeojson());
} else {
dto.setMapGeojson(parseGeojson(exist.getMapGeojson()));
}
return dto;
}
private Object parseGeojson(String geojson) {
if (StringUtil.isBlank(geojson)) {
return null;
}
return JsonUtils.fromJson(geojson, Object.class);
}
private String serializeGeojson(Object geojson) {
if (geojson == null) {
return null;
}
if (geojson instanceof String) {
String json = ((String) geojson).trim();
return json.isEmpty() ? null : json;
}
return JsonUtils.toJson(geojson);
}
private String nullToEmpty(String value) {
return value == null ? "" : value;
}
}
package com.liquidnet.service.sweet.service.impl; package com.liquidnet.service.sweet.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.sweet.entity.SweetManual; import com.liquidnet.service.sweet.constant.SweetManualPosition;
import com.liquidnet.service.sweet.entity.SweetManualSort; import com.liquidnet.service.sweet.entity.SweetManualSort;
import com.liquidnet.service.sweet.mapper.SweetManualSortMapper; import com.liquidnet.service.sweet.mapper.SweetManualSortMapper;
import com.liquidnet.service.sweet.service.ISweetManualSortService; import com.liquidnet.service.sweet.service.ISweetManualSortService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liquidnet.service.sweet.utils.RedisDataUtils; import com.liquidnet.service.sweet.utils.RedisDataUtils;
import com.liquidnet.service.sweet.vo.SweetManualSortOptionVo;
import com.liquidnet.service.sweet.vo.SweetManualSortVo;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
/** /**
* <p> * <p>
...@@ -30,9 +33,11 @@ public class SweetManualSortServiceImpl extends ServiceImpl<SweetManualSortMappe ...@@ -30,9 +33,11 @@ public class SweetManualSortServiceImpl extends ServiceImpl<SweetManualSortMappe
private RedisDataUtils redisDataUtils; private RedisDataUtils redisDataUtils;
@Override @Override
public ResponseDto<SweetManualSort> get(String manualId) { public ResponseDto<SweetManualSortVo> get(String manualId) {
try { try {
return ResponseDto.success(sweetManualSortMapper.selectOne(Wrappers.lambdaQuery(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId))); SweetManualSort sort = sweetManualSortMapper.selectOne(
Wrappers.lambdaQuery(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId));
return ResponseDto.success(toVo(sort, manualId));
} catch (Exception e) { } catch (Exception e) {
return ResponseDto.failure(); return ResponseDto.failure();
} }
...@@ -41,24 +46,50 @@ public class SweetManualSortServiceImpl extends ServiceImpl<SweetManualSortMappe ...@@ -41,24 +46,50 @@ public class SweetManualSortServiceImpl extends ServiceImpl<SweetManualSortMappe
@Override @Override
public ResponseDto<Boolean> add(String manualId, String content) { public ResponseDto<Boolean> add(String manualId, String content) {
try { try {
int count = sweetManualSortMapper.selectCount(Wrappers.lambdaUpdate(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId)); String normalizedContent = SweetManualPosition.normalizeContent(content);
int count = sweetManualSortMapper.selectCount(
Wrappers.lambdaUpdate(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId));
if (count > 0) { if (count > 0) {
SweetManualSort sweetManualSort = SweetManualSort.getNew(); SweetManualSort sweetManualSort = SweetManualSort.getNew();
sweetManualSort.setManualId(manualId); sweetManualSort.setManualId(manualId);
sweetManualSort.setShowPosition(content); sweetManualSort.setShowPosition(normalizedContent);
sweetManualSort.setUpdatedAt(LocalDateTime.now()); sweetManualSort.setUpdatedAt(LocalDateTime.now());
sweetManualSortMapper.update(sweetManualSort, Wrappers.lambdaUpdate(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId)); sweetManualSortMapper.update(sweetManualSort,
Wrappers.lambdaUpdate(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId));
} else { } else {
SweetManualSort sweetManualSort = SweetManualSort.getNew(); SweetManualSort sweetManualSort = SweetManualSort.getNew();
sweetManualSort.setManualId(manualId); sweetManualSort.setManualId(manualId);
sweetManualSort.setShowPosition(content); sweetManualSort.setShowPosition(normalizedContent);
sweetManualSort.setCreatedAt(LocalDateTime.now()); sweetManualSort.setCreatedAt(LocalDateTime.now());
sweetManualSortMapper.insert(sweetManualSort); sweetManualSortMapper.insert(sweetManualSort);
} }
redisDataUtils.deleteSortRedisData(manualId); redisDataUtils.deleteSortRedisData(manualId);
} catch (IllegalArgumentException e) {
return ResponseDto.failure(e.getMessage());
} catch (Exception e) { } catch (Exception e) {
return ResponseDto.failure(); return ResponseDto.failure();
} }
return ResponseDto.success(); return ResponseDto.success();
} }
@Override
public ResponseDto<List<SweetManualSortOptionVo>> options() {
return ResponseDto.success(SweetManualPosition.allOptions());
}
private SweetManualSortVo toVo(SweetManualSort sort, String manualId) {
SweetManualSortVo vo = new SweetManualSortVo();
vo.setManualId(manualId);
vo.setOptions(SweetManualPosition.allOptions());
if (sort == null) {
vo.setPositions(SweetManualPosition.parsePositions(null));
return vo;
}
vo.setMid(sort.getMid());
vo.setShowPosition(sort.getShowPosition());
vo.setPositions(SweetManualPosition.parsePositions(sort.getShowPosition()));
vo.setCreatedAt(sort.getCreatedAt());
vo.setUpdatedAt(sort.getUpdatedAt());
return vo;
}
} }
...@@ -7,7 +7,9 @@ import com.liquidnet.common.cache.redis.util.RedisUtil; ...@@ -7,7 +7,9 @@ import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.util.CollectionUtil; import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.DateUtil; import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.commons.lang.util.IDGenerator; import com.liquidnet.commons.lang.util.IDGenerator;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.RandomUtil; import com.liquidnet.commons.lang.util.RandomUtil;
import com.liquidnet.commons.lang.util.StringUtil;
import com.liquidnet.service.base.SqlMapping; import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst; import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.base.constant.RedisKeyExpireConst; import com.liquidnet.service.base.constant.RedisKeyExpireConst;
...@@ -179,6 +181,47 @@ public class RedisDataUtils { ...@@ -179,6 +181,47 @@ public class RedisDataUtils {
} }
} }
public SweetManualExtConfigDto setManualExtConfigRedisData(String manualId, SweetManualExtConfigDto content) {
String redisKey = SweetConstant.REDIS_KEY_SWEET_MANUAL_EXT_CONFIG.concat(manualId);
SweetManualExtConfigDto configDto;
if (content != null) {
configDto = content;
} else {
SweetManual manual = sweetManualMapper.selectOne(
Wrappers.lambdaQuery(SweetManual.class).eq(SweetManual::getManualId, manualId));
configDto = new SweetManualExtConfigDto();
if (manual != null) {
configDto.setManualId(manual.getManualId());
configDto.setFoodGuideUrl(manual.getFoodGuideUrl());
configDto.setAlbumUrl(manual.getAlbumUrl());
configDto.setLostFoundWjxId(manual.getLostFoundWjxId());
configDto.setMapGeojson(parseManualMapGeojson(manual.getMapGeojson()));
}
}
redisUtil.set(redisKey, configDto);
return configDto;
}
public SweetManualExtConfigDto getManualExtConfigRedisData(String manualId) {
String redisKey = SweetConstant.REDIS_KEY_SWEET_MANUAL_EXT_CONFIG.concat(manualId);
Object obj = redisUtil.get(redisKey);
if (obj == null) {
return setManualExtConfigRedisData(manualId, null);
}
return (SweetManualExtConfigDto) obj;
}
public void deleteManualExtConfigRedisData(String manualId) {
redisUtil.del(SweetConstant.REDIS_KEY_SWEET_MANUAL_EXT_CONFIG.concat(manualId));
}
private Object parseManualMapGeojson(String geojson) {
if (StringUtil.isBlank(geojson)) {
return null;
}
return JsonUtils.fromJson(geojson, Object.class);
}
public List<String> setTagRedisData(String manualId) { public List<String> setTagRedisData(String manualId) {
String redisKey = SweetConstant.REDIS_KEY_SWEET_MANUAL_SORT.concat(manualId); String redisKey = SweetConstant.REDIS_KEY_SWEET_MANUAL_SORT.concat(manualId);
SweetManualSort data = sweetManualSortMapper.selectOne(Wrappers.lambdaQuery(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId)); SweetManualSort data = sweetManualSortMapper.selectOne(Wrappers.lambdaQuery(SweetManualSort.class).eq(SweetManualSort::getManualId, manualId));
......
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