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

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

Merge branch 'master' into dev-1.6-shouqianba

# Conflicts:
#	liquidnet-bus-common/liquidnet-common-service-base/src/main/java/com/liquidnet/service/base/constant/RedisKeyExpireConst.java
#	liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/service/impl/GoblinFrontServiceImpl.java
parents 461ce778 f8932630
This diff is collapsed.
-- ----------------------------
-- 1. 护照实体表 (预先生成,供用户扫码绑定)
-- ----------------------------
CREATE TABLE `adam_caomei_passport` (
`mid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`passport_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '护照编号 (全局唯一,用于生成二维码)',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '绑定状态: 0-未绑定, 1-已绑定, 2-已作废',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '绑定的用户ID',
`bound_at` datetime DEFAULT NULL COMMENT '首次绑定时间',
`unbound_at` datetime DEFAULT NULL COMMENT '最近一次解绑时间 (后台操作解绑时记录)',
`batch_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '生成/印刷批次号 (便于后期溯源管理)',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '后台运营备注',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_passport_no` (`passport_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='草莓护照-实体编号表';
-- ----------------------------
-- 2. 徽章配置表 (运营后台配置,已移除 claimed_count,后台列表实时 COUNT 统计)
-- ----------------------------
CREATE TABLE `adam_caomei_badge` (
`mid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`badge_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '徽章ID',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '徽章名称',
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '徽章图标 (Emoji字符或图片URL)',
`type` tinyint(4) NOT NULL COMMENT '徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章',
`performance_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '关联演出ID (仅演出纪念徽章必填,其他类型为空)',
`display_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '上架状态: 0-下架(默认), 1-已发布',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`mid`),
KEY `idx_badge_id` (`badge_id`),
KEY `idx_type_status` (`type`,`display_status`),
KEY `idx_performance_id` (`performance_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='草莓护照-徽章配置表';
-- ----------------------------
-- 3. 用户徽章获得记录表 (支撑用户徽章墙与后台领取人数统计)
-- ----------------------------
CREATE TABLE `adam_caomei_user_badge` (
`mid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户id',
`badge_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '徽章ID',
`source` tinyint(4) NOT NULL COMMENT '获取途径: 1-绑定护照自动发放, 2-购票自动发放, 3-补签审核通过, 4-现场管理员手动发放',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获得时间',
PRIMARY KEY (`mid`),
UNIQUE KEY `uk_user_badge` (`user_id`,`badge_id`) COMMENT '联合唯一索引: 同一用户同一徽章只能获得一次',
KEY `idx_badge_id` (`badge_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='草莓护照-用户徽章获得记录表';
-- ----------------------------
-- 4. 徽章补签申请记录表 (支撑补签审核流程)
-- ----------------------------
CREATE TABLE `adam_caomei_badge_apply_record` (
`mid` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`apply_record_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '补签id',
`user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '用户id',
`badge_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '申请补签的徽章ID',
`performance_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '关联的演出ID (冗余字段,便于后台筛选)',
`proof_image_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单截图/凭证图片URL',
`audit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '审核状态: 0-待审核, 1-已通过, 2-已驳回',
`reject_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '驳回理由 (驳回时必填,用户端可见)',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '申请时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '审核/更新时间',
PRIMARY KEY (`mid`),
KEY `idx_record_id`(`apply_record_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_audit_status` (`audit_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='草莓护照-徽章补签申请记录表';
-- 2026-04-13 新增排序字段
ALTER TABLE `adam_caomei_badge` ADD COLUMN `sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序值,数值越大越靠前' AFTER `display_status`;
-- 2026-04-16 新增分享文案字段
ALTER TABLE `adam_caomei_badge` ADD COLUMN `share_text` varchar(255) NOT NULL DEFAULT '' COMMENT '徽章分享文案' AFTER `sort`;
-- 2026-04-23 新增徽章副标题字段
ALTER TABLE `adam_caomei_badge` ADD COLUMN `sub_title` varchar(32) NOT NULL DEFAULT '' COMMENT '徽章副标题(最多20字)' AFTER `name`;
-- TODO 上线前需要运行:adam_caomei_passport_inserts_BATCH-CAOMEI-B1.sql
\ No newline at end of file
......@@ -20,6 +20,12 @@ public class AdamRedisConst {
public static final String INFO_THIRD_PARTY = PREFIX.concat("info:third_party:");
public static final String INFO_ENTERS = PREFIX.concat("info:enters:");
public static final String INFO_ADDRESSES = PREFIX.concat("info:addresses:");
public static final String INFO_CAOMEI_BADGE_PUBLISHED = PREFIX.concat("info:caomei:badge:published");
public static final String INFO_CAOMEI_BADGE_USER = PREFIX.concat("info:caomei:badge:user:");
/**
* 身份证号已支付演出ID列表(短缓存,缓解 kylin_order_ticket_entities 无身份证索引时的热点查询压力)
*/
public static final String INFO_CAOMEI_PAID_PERFORMANCE_IDS_BY_IDCARD = PREFIX.concat("info:caomei:paid_performance_ids:idcard:");
/**
* {adam:info:biz:{uid},List<com.liquidnet.service.adam.dto.vo.AdamUserBizAcctVo>}
*/
......@@ -36,6 +42,12 @@ public class AdamRedisConst {
*/
public static final String INFO_CERTIFICATION_JUNK = PREFIX.concat("info:certification_junk:");
/**
* 身份证号 -> 已绑定实名的 uid(用于快速判重:同一身份证只能实名一个账号)
* value: uid(String)
*/
public static final String INFO_REAL_NAME_UID_BY_IDCARD = PREFIX.concat("info:real_name:uid_by_idcard:");
public static final String INFO_MEMBER_JOINUS = PREFIX.concat("info:member:joinus:");
public static final String INFO_MEMBER_SIMPLE = PREFIX.concat("info:member:simple");
public static final String INFO_MEMBER_CATEGORY = PREFIX.concat("info:member:category:");
......
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("草莓护照-补签审核搜索参数")
public class AdamCaomeiBadgeApplyAuditSearchParam {
@ApiModelProperty(value = "用户名/姓名")
private String userName;
@ApiModelProperty(value = "审核状态:0-待审核 1-已通过 2-已驳回")
private Integer auditStatus;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel("草莓徽章-补签申请参数")
public class AdamCaomeiBadgeApplyParam {
@NotBlank(message = "徽章ID不能为空")
@ApiModelProperty(value = "徽章ID", required = true)
private String badgeId;
@NotBlank(message = "凭证图片不能为空")
@ApiModelProperty(value = "订单截图/凭证图片URL", required = true)
private String proofImageUrl;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
@ApiModel("草莓徽章-认领参数")
public class AdamCaomeiBadgeClaimParam {
@NotEmpty(message = "徽章ID列表不能为空")
@ApiModelProperty(value = "徽章ID列表", required = true)
private List<@NotBlank(message = "徽章ID不能为空") String> badgeIds;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("草莓护照-徽章领取用户搜索参数")
public class AdamCaomeiBadgeClaimUserSearchParam {
@ApiModelProperty(value = "徽章ID")
private String badgeId;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 草莓护照-徽章管理参数
*/
@Data
@ApiModel("草莓护照-徽章管理参数")
public class AdamCaomeiBadgeParam {
@ApiModelProperty(value = "主键ID")
private Long mid;
@ApiModelProperty(value = "徽章ID")
private String badgeId;
@ApiModelProperty(value = "徽章名称")
private String name;
@ApiModelProperty(value = "徽章副标题")
private String subTitle;
@ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)")
private String icon;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章")
private Integer type;
@ApiModelProperty(value = "关联演出ID (仅演出纪念徽章必填,其他类型为空)")
private String performanceId;
@ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布")
private Integer displayStatus;
@ApiModelProperty(value = "排序值,数值越大越靠前")
private Integer sort;
@ApiModelProperty(value = "徽章分享文案")
private String shareText;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 草莓护照-徽章搜索参数
*/
@Data
@ApiModel("草莓护照-徽章搜索参数")
public class AdamCaomeiBadgeSearchParam {
@ApiModelProperty(value = "徽章名称")
private String name;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章")
private Integer type;
@ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布")
private Integer displayStatus;
@ApiModelProperty(value = "添加时间起(yyyy-MM-dd)")
private String createdAtBegin;
@ApiModelProperty(value = "添加时间止(yyyy-MM-dd)")
private String createdAtEnd;
@ApiModelProperty(value = "当前页码")
private Integer pageNum = 1;
@ApiModelProperty(value = "每页数量")
private Integer pageSize = 10;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
@Data
@ApiModel("草莓护照-编号参数")
public class AdamCaomeiPassportNoParam {
@NotBlank(message = "护照编码不能为空")
@ApiModelProperty(value = "护照实体编号(扫码或手输)", required = true)
private String passportNo;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("草莓护照-护照管理搜索参数")
public class AdamCaomeiPassportSearchParam {
@ApiModelProperty(value = "护照编号")
private String passportNo;
@ApiModelProperty(value = "关联用户名(昵称/真实姓名模糊)")
private String userName;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "证件号")
private String idCard;
@ApiModelProperty(value = "绑定状态:0-未绑定 1-已绑定 2-已作废,空为全部")
private Integer bindStatus;
}
package com.liquidnet.service.adam.dto.param;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("二要素认证参数")
public class AdamIdentityParam {
@ApiModelProperty(value = "姓名(手动填写时传)")
private String name;
@ApiModelProperty(value = "证件号(手动填写时传)")
private String idCard;
@ApiModelProperty(value = "入场人ID(选择观演人时传)")
private String entersId;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
@ApiModel("草莓护照-补签审核详情")
public class AdamCaomeiBadgeApplyAuditDetailVo {
@ApiModelProperty(value = "申请ID")
private String applyRecordId;
@ApiModelProperty(value = "申请账号")
private String userId;
@ApiModelProperty(value = "用户名")
private String nickname;
@ApiModelProperty(value = "姓名")
private String realName;
@ApiModelProperty(value = "证件号(脱敏)")
private String idCard;
@ApiModelProperty(value = "申请徽章ID")
private String badgeId;
@ApiModelProperty(value = "申请徽章")
private String badgeName;
@ApiModelProperty(value = "关联演出ID")
private String performanceId;
@ApiModelProperty(value = "关联演出")
private String performanceName;
@ApiModelProperty(value = "申请附件")
private String proofImageUrl;
@ApiModelProperty(value = "申请附件 URL 列表(解析自原始字段)")
private List<String> proofImageUrls;
@ApiModelProperty(value = "审核状态:0-待审核 1-已通过 2-已驳回")
private Integer auditStatus;
@ApiModelProperty(value = "驳回理由")
private String rejectReason;
@ApiModelProperty(value = "申请时间")
private Date createdAt;
@ApiModelProperty(value = "审核时间")
private Date updatedAt;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
@ApiModel("草莓护照-补签审核列表")
public class AdamCaomeiBadgeApplyAuditVo {
@ApiModelProperty(value = "申请ID")
private String applyRecordId;
@ApiModelProperty(value = "申请账号")
private String userId;
@ApiModelProperty(value = "用户名")
private String nickname;
@ApiModelProperty(value = "姓名")
private String realName;
@ApiModelProperty(value = "证件号(脱敏)")
private String idCard;
@ApiModelProperty(value = "申请徽章")
private String badgeName;
@ApiModelProperty(value = "关联演出")
private String performanceName;
@ApiModelProperty(value = "申请附件")
private String proofImageUrl;
@ApiModelProperty(value = "申请附件 URL 列表(解析自原始字段)")
private List<String> proofImageUrls;
@ApiModelProperty(value = "审核状态:0-待审核 1-已通过 2-已驳回")
private Integer auditStatus;
@ApiModelProperty(value = "申请时间")
private Date createdAt;
}
package com.liquidnet.service.adam.dto.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.liquidnet.commons.lang.util.DateUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓徽章-补签记录(用户端)")
public class AdamCaomeiBadgeApplyRecordUserVo {
@ApiModelProperty("补签申请ID")
private String applyRecordId;
@ApiModelProperty("申请徽章名称")
private String badgeName;
@ApiModelProperty("申请时间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateUtil.DATE_FULL_STR)
private Date applyTime;
@ApiModelProperty("审核状态:0-待审核 1-已通过 2-已驳回")
private Integer auditStatus;
@ApiModelProperty("驳回理由")
private String rejectReason;
@ApiModelProperty("是否可重新上传(仅驳回状态为 true)")
private Boolean canReupload;
@ApiModelProperty("凭证图片")
private String proofImageUrl;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓护照-徽章领取用户")
public class AdamCaomeiBadgeClaimUserVo {
@ApiModelProperty(value = "用户昵称")
private String nickname;
@ApiModelProperty(value = "手机号(脱敏)")
private String mobile;
@ApiModelProperty(value = "领取时间")
private Date claimedAt;
@ApiModelProperty(value = "获取途径: 1-绑定护照自动发放, 2-购票自动发放, 3-补签审核通过, 4-现场管理员手动发放")
private Integer source;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* 草莓护照-徽章视图对象
*/
@Data
@ApiModel("草莓护照-徽章视图对象")
public class AdamCaomeiBadgeVo {
@ApiModelProperty(value = "主键ID")
private Long mid;
@ApiModelProperty(value = "徽章ID")
private String badgeId;
@ApiModelProperty(value = "徽章名称")
private String name;
@ApiModelProperty(value = "徽章副标题")
private String subTitle;
@ApiModelProperty(value = "徽章图标 (Emoji字符或图片URL)")
private String icon;
@ApiModelProperty(value = "徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章")
private Integer type;
@ApiModelProperty(value = "关联演出ID")
private String performanceId;
@ApiModelProperty(value = "上架状态: 0-下架(默认), 1-已发布")
private Integer displayStatus;
@ApiModelProperty(value = "排序值,数值越大越靠前")
private Integer sort;
@ApiModelProperty(value = "徽章分享文案")
private String shareText;
@ApiModelProperty(value = "添加时间")
private Date createdAt;
@ApiModelProperty(value = "更新时间")
private Date updatedAt;
@ApiModelProperty(value = "领取人数")
private Integer claimedCount;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓护照-用户已领徽章项")
public class AdamCaomeiPassportBadgeItemVo {
@ApiModelProperty(value = "徽章ID")
private String badgeId;
@ApiModelProperty(value = "徽章名称")
private String badgeName;
@ApiModelProperty(value = "图标")
private String icon;
@ApiModelProperty(value = "领取时间")
private Date claimedAt;
@ApiModelProperty(value = "获取途径 source")
private Integer source;
}
package com.liquidnet.service.adam.dto.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.liquidnet.commons.lang.util.DateUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓护照-货架徽章项(含认领状态与交互提示)")
public class AdamCaomeiPassportBadgeShelfItemVo {
@ApiModelProperty("徽章ID")
private String badgeId;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("副标题")
private String subTitle;
@ApiModelProperty("图标")
private String icon;
@ApiModelProperty("分享文案")
private String shareText;
@ApiModelProperty("类型 1护照 2演出 3特殊")
private Integer type;
@ApiModelProperty("关联演出ID(演出纪念徽章)")
private String performanceId;
@ApiModelProperty("关联演出名称(type=2 时用于按演出分组展示;无数据时可能为演出ID)")
private String performanceName;
@ApiModelProperty("是否已认领")
private boolean claimed;
@ApiModelProperty("认领时间(未认领为空)")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateUtil.DATE_FULL_STR)
private Date claimedAt;
@ApiModelProperty("是否可认领(护照徽章未发放完全,或演出徽章有票未领)")
private boolean claimable;
@ApiModelProperty("是否存在补签待审核(仅演出徽章生效)")
private boolean applyPending;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
@ApiModel("草莓护照-护照详情")
public class AdamCaomeiPassportDetailVo {
@ApiModelProperty(value = "护照主键")
private Long mid;
@ApiModelProperty(value = "护照编号")
private String passportNo;
@ApiModelProperty(value = "绑定状态")
private Integer status;
@ApiModelProperty(value = "绑定用户uid")
private String userId;
@ApiModelProperty(value = "首次绑定时间")
private Date boundAt;
@ApiModelProperty(value = "批次号")
private String batchNo;
@ApiModelProperty(value = "用户昵称")
private String nickname;
@ApiModelProperty(value = "手机号(脱敏)")
private String mobile;
@ApiModelProperty(value = "真实姓名")
private String realName;
@ApiModelProperty(value = "证件号(脱敏)")
private String idCard;
@ApiModelProperty(value = "已领取徽章")
private List<AdamCaomeiPassportBadgeItemVo> badges;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel("草莓护照-C端首页聚合")
public class AdamCaomeiPassportHomeVo {
@ApiModelProperty("个人信息卡片")
private AdamCaomeiPassportUserCardVo userCard;
@ApiModelProperty("已认领徽章(全部获得记录,用于网格墙)")
private List<AdamCaomeiPassportUserClaimedBadgeVo> claimedBadges;
@ApiModelProperty("全部上架徽章(扁平列表;演出类含 performanceName,前端可按类型或按演出分组展示)")
private List<AdamCaomeiPassportBadgeShelfItemVo> allBadges;
}
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("草莓护照-护照管理列表")
public class AdamCaomeiPassportListVo {
@ApiModelProperty(value = "主键")
private Long mid;
@ApiModelProperty(value = "护照ID/编号")
private String passportNo;
@ApiModelProperty(value = "关联用户昵称")
private String nickname;
@ApiModelProperty(value = "真实姓名")
private String realName;
@ApiModelProperty(value = "手机号(脱敏)")
private String mobile;
@ApiModelProperty(value = "证件号(脱敏)")
private String idCard;
@ApiModelProperty(value = "绑定状态 0未绑定 1已绑定 2已作废")
private Integer bindStatus;
@ApiModelProperty(value = "已领取徽章数")
private Integer claimedBadgeCount;
}
package com.liquidnet.service.adam.dto.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.liquidnet.commons.lang.util.DateUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓护照-首页个人信息卡片")
public class AdamCaomeiPassportUserCardVo {
@ApiModelProperty("头像")
private String avatar;
@ApiModelProperty("昵称")
private String nickname;
@ApiModelProperty("是否已实名认证")
private boolean realNameVerified;
@ApiModelProperty("护照编号(未绑定时为空)")
private String passportNo;
@ApiModelProperty("护照获得时间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateUtil.DATE_FULL_STR)
private Date passportClaimedAt;
@ApiModelProperty("是否已绑定实体护照")
private boolean passportBound;
}
package com.liquidnet.service.adam.dto.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.liquidnet.commons.lang.util.DateUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@Data
@ApiModel("草莓护照-已认领徽章(墙)")
public class AdamCaomeiPassportUserClaimedBadgeVo {
@ApiModelProperty("徽章ID")
private String badgeId;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty("副标题")
private String subTitle;
@ApiModelProperty("图标")
private String icon;
@ApiModelProperty("分享文案")
private String shareText;
@ApiModelProperty("类型 1护照类型徽章 2演出类型徽章 3特殊徽章")
private Integer type;
@ApiModelProperty("关联演出名称(仅 type=2 有值)")
private String performanceName;
@ApiModelProperty("获得时间")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DateUtil.DATE_FULL_STR)
private Date claimedAt;
@ApiModelProperty("获取途径: 1-绑定护照自动发放, 2-购票自动发放, 3-补签审核通过, 4-现场管理员手动发放")
private Integer source;
}
package com.liquidnet.service.adam.service;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyRecordUserVo;
import com.liquidnet.service.base.ResponseDto;
import java.util.List;
/**
* 草莓徽章(用户端)
*/
public interface IAdamCaomeiBadgeUserService {
/**
* 认领徽章
*/
ResponseDto<List<String>> claimBadges(List<String> badgeIds, String uid);
/**
* 补签申请记录列表(用户端)
*/
ResponseDto<List<AdamCaomeiBadgeApplyRecordUserVo>> getApplyRecords(String uid);
/**
* 发起补签申请(驳回后可再次提交)
*/
ResponseDto<String> applyBadge(AdamCaomeiBadgeApplyParam param, String uid);
}
package com.liquidnet.service.adam.service;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportHomeVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportUserClaimedBadgeVo;
import com.liquidnet.service.base.ResponseDto;
import java.util.List;
/**
* 草莓护照(用户端)
*/
public interface IAdamCaomeiPassportUserService {
/**
* 绑定实体护照:校验编号有效性、是否可绑;通过则写入并发放已上架护照纪念徽章(type=1)
*/
ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo);
/**
* 护照首页:个人信息、实名状态、已认领墙、按类型分组的全部上架徽章
*/
ResponseDto<AdamCaomeiPassportHomeVo> getPassportHome();
/**
* 校验当前登录用户是否已绑定护照
*/
ResponseDto<Boolean> checkPassportBound();
}
......@@ -72,4 +72,13 @@ public interface IAdamUserService {
AdamRealInfoVo identityForUpsert(String uid, String name, String idCard, String mobile, boolean updateFlg);
AdamRealInfoVo identityForUpdate(String uid, String mobile, int idType, int node, String idCard, String idName);
/**
* 二要素认证
* @param uid
* @param name
* @param idCard
* @return
*/
AdamRealInfoVo verifyTwoElements(String uid, String name, String idCard);
}
......@@ -126,6 +126,18 @@ public class BaseController
return rspData;
}
/**
* 响应分页数据(用于查询结果已转为 VO 列表,但需保留 PageHelper 总条数的场景)
*/
protected TableDataInfo getDataTable(PageInfo<?> pageInfo)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(0);
rspData.setRows(pageInfo.getList());
rspData.setTotal(pageInfo.getTotal());
return rspData;
}
/**
* 响应返回结果
*
......
package com.liquidnet.client.admin.web.controller.zhengzai.adam;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiBadgeApplyAuditAdminService;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyAuditSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyAuditDetailVo;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Api(tags = "草莓护照-补签审核")
@Controller
@RequestMapping("adam/caomei/badgeApplyAudit")
public class AdamCaomeiBadgeApplyAuditController extends BaseController {
private final String prefix = "zhengzai/adam/caomei/badgeApplyAudit";
@Autowired
private IAdamCaomeiBadgeApplyAuditAdminService badgeApplyAuditAdminService;
@RequiresPermissions("adam:caomei:badgeApplyAudit:list")
@GetMapping()
public String view() {
return prefix + "/list";
}
@RequiresPermissions("adam:caomei:badgeApplyAudit:list")
@PostMapping("list")
@ResponseBody
public TableDataInfo list(AdamCaomeiBadgeApplyAuditSearchParam param) {
startPage();
PageInfo<?> pageInfo = badgeApplyAuditAdminService.listApplyAudits(param);
return getDataTable(pageInfo);
}
@RequiresPermissions("adam:caomei:badgeApplyAudit:list")
@GetMapping("detail/{applyRecordId}")
public String detail(@PathVariable("applyRecordId") String applyRecordId, ModelMap mmap) {
AdamCaomeiBadgeApplyAuditDetailVo detail = badgeApplyAuditAdminService.getApplyAuditDetail(applyRecordId);
mmap.put("detail", detail);
return prefix + "/detail";
}
@RequiresPermissions("adam:caomei:badgeApplyAudit:edit")
@Log(title = "草莓护照-补签审核:通过", businessType = BusinessType.UPDATE)
@PostMapping("pass")
@ResponseBody
public AjaxResult pass(@RequestParam("applyRecordId") String applyRecordId) {
boolean ok = badgeApplyAuditAdminService.passApplyAudit(applyRecordId);
if (!ok) {
return error("审核通过失败:申请不存在或当前状态不可操作");
}
return success();
}
@RequiresPermissions("adam:caomei:badgeApplyAudit:edit")
@Log(title = "草莓护照-补签审核:驳回", businessType = BusinessType.UPDATE)
@PostMapping("reject")
@ResponseBody
public AjaxResult reject(@RequestParam("applyRecordId") String applyRecordId,
@RequestParam("rejectReason") String rejectReason) {
if (StringUtils.isBlank(rejectReason)) {
return error("驳回理由不能为空");
}
boolean ok = badgeApplyAuditAdminService.rejectApplyAudit(applyRecordId, rejectReason);
if (!ok) {
return error("驳回失败:申请不存在或当前状态不可操作");
}
return success();
}
}
package com.liquidnet.client.admin.web.controller.zhengzai.adam;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiPassportAdminService;
import com.liquidnet.service.adam.dto.param.AdamCaomeiPassportSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportDetailVo;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.Api;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
@Api(tags = "草莓护照-护照管理")
@Controller
@RequestMapping("adam/caomei/passport")
public class AdamCaomeiPassportController extends BaseController {
private final String prefix = "zhengzai/adam/caomei/passport";
@Autowired
private IAdamCaomeiPassportAdminService adamCaomeiPassportAdminService;
@RequiresPermissions("adam:caomei:passport:list")
@GetMapping()
public String view() {
return prefix + "/passport_list";
}
@RequiresPermissions("adam:caomei:passport:list")
@PostMapping("list")
@ResponseBody
public TableDataInfo list(AdamCaomeiPassportSearchParam param) {
startPage();
PageInfo<?> pageInfo = adamCaomeiPassportAdminService.listPassports(param);
return getDataTable(pageInfo);
}
@RequiresPermissions("adam:caomei:passport:list")
@GetMapping("bound/count")
@ResponseBody
public AjaxResult boundCount() {
return AjaxResult.success(adamCaomeiPassportAdminService.countBoundPassports());
}
@RequiresPermissions("adam:caomei:passport:list")
@GetMapping("detail/{passportNo}")
public String detail(@PathVariable("passportNo") String passportNo, ModelMap mmap) {
AdamCaomeiPassportDetailVo detail = adamCaomeiPassportAdminService.getPassportDetail(passportNo);
mmap.put("detail", detail);
return prefix + "/passport_detail";
}
@RequiresPermissions("adam:caomei:passport:edit")
@Log(title = "草莓护照-护照解绑", businessType = BusinessType.UPDATE)
@PostMapping("unbind")
@ResponseBody
public AjaxResult unbind(@RequestParam("passportNo") String passportNo) {
boolean ok = adamCaomeiPassportAdminService.unbindPassport(passportNo);
if (!ok) {
return error("解绑失败:护照不存在或当前未绑定用户");
}
return success();
}
}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('新增徽章')" />
<th:block th:include="include :: bootstrap-fileinput-css" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-badge-add">
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章名称:</label>
<div class="col-sm-8">
<input name="name" class="form-control" type="text" maxlength="50" required placeholder="最多50个字">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">徽章副标题:</label>
<div class="col-sm-8">
<input name="subTitle" class="form-control" type="text" maxlength="20" placeholder="最多20个字">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章图标:</label>
<div class="col-sm-8">
<input type="hidden" name="icon" id="badgeIconUrl" value="">
<div id="badgeIconPreviewWrap" class="m-b" style="display:none;">
<img id="badgeIconPreview" src="" alt="预览" style="max-height:96px;border-radius:4px;border:1px solid #eee;"/>
</div>
<div class="file-loading">
<input id="fileinput-badge-icon" type="file" name="file" data-browse-on-zone-click="true">
</div>
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 支持 jpg、png、gif、webp,单张最大 5M;上传后自动保存为图片地址。</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章类型:</label>
<div class="col-sm-8">
<select name="type" class="form-control m-b" required onchange="typeChange(this.value)">
<option value="">请选择</option>
<option value="1">护照纪念徽章</option>
<option value="2">演出纪念徽章</option>
<!-- <option value="3">特殊徽章</option> -->
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序:</label>
<div class="col-sm-8">
<input name="sort" class="form-control" type="number" min="0" value="0" placeholder="数值越大越靠前">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">分享文案:</label>
<div class="col-sm-8">
<textarea name="shareText" class="form-control" rows="3" maxlength="255" placeholder="请输入徽章分享文案"></textarea>
</div>
</div>
<div class="form-group" id="ticketTimesDiv" style="display: none;">
<label class="col-sm-3 control-label is-required">关联演出:</label>
<div class="col-sm-8">
<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>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: bootstrap-fileinput-js" />
<script type="text/javascript" th:inline="javascript">
var prefix = ctx + "adam/caomei/badge";
var platformUrl = /*[[${platformUrl}]]*/ '';
function typeChange(val) {
if (val == 2) {
$("#ticketTimesDiv").show();
$("#performanceId").prop("required", true);
} else {
$("#ticketTimesDiv").hide();
$("#performanceId").prop("required", false);
$("#performanceId").val("");
}
}
$("#form-badge-add").validate({
focusCleanup: true
});
function submitHandler() {
if (!$("#badgeIconUrl").val()) {
$.modal.msgWarning("请先上传徽章图标");
return;
}
if ($.validate.form()) {
$.operate.save(prefix + "/add", $('#form-badge-add').serialize());
}
}
$(function () {
$("#fileinput-badge-icon").fileinput({
'theme': 'explorer-fas',
'uploadUrl': platformUrl + "/platform/basicServices/alOss/upload/unsm",
"uploadExtraData": {
"pathName": "other",
"buckType": 1
},
autoReplace: true,
dropZoneTitle: "点击或拖拽上传徽章图标",
maxFileCount: 1,
maxFileSize: 5120,
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'],
msgSizeTooLarge: '文件 "{name}" ({size} KB) 超过了允许大小 {maxSize} KB,最大支持上传5M文件'
}).on("filebatchselected", function () {
$(this).fileinput("upload");
}).on("fileuploaded", function (event, data) {
if (!data.response || !data.response.data || !data.response.data.ossPath) {
$.modal.alertWarning((data.response && data.response.msg) ? data.response.msg : "图片上传失败");
return;
}
var fullUrl = "https://img.zhengzai.tv/" + data.response.data.ossPath;
$("#badgeIconUrl").val(fullUrl);
$("#badgeIconPreview").attr("src", fullUrl);
$("#badgeIconPreviewWrap").show();
});
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('领取用户列表')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<input type="hidden" id="badgeId" th:value="${badgeId}">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var prefix = ctx + "adam/caomei/badge";
$(function() {
var options = {
url: prefix + "/claimUsers/list",
queryParams: function(params) {
var curParams = {
pageSize: params.limit,
pageNum: params.offset / params.limit + 1,
searchValue: params.search,
orderByColumn: params.sort,
isAsc: params.order,
badgeId: $("#badgeId").val()
};
return curParams;
},
modalName: "领取用户",
columns: [
{
field: 'nickname',
title: '用户昵称'
},
{
field: 'mobile',
title: '手机号'
},
{
field: 'claimedAt',
title: '领取时间'
},
{
field: 'source',
title: '获取途径',
formatter: function(value, row, index) {
if (value == 1) return '绑定护照自动发放';
if (value == 2) return '购票自动发放';
if (value == 3) return '补签审核通过';
if (value == 4) return '现场管理员手动发放';
return '未知';
}
}
]
};
$.table.init(options);
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('徽章详情')" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-badge-detail" th:object="${badge}">
<div class="form-group">
<label class="col-sm-3 control-label">徽章名称:</label>
<div class="col-sm-8">
<div class="form-control-static" th:text="*{name}"></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">徽章副标题:</label>
<div class="col-sm-8">
<div class="form-control-static" th:text="*{subTitle != null and subTitle != '' ? subTitle : '-'}"></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">徽章图标:</label>
<div class="col-sm-8">
<div class="form-control-static">
<span th:if="*{icon != null and (!icon.startsWith('http') and !icon.startsWith('/'))}" style="font-size: 24px;" th:text="*{icon}"></span>
<img th:if="*{icon != null and (icon.startsWith('http') or icon.startsWith('/'))}" th:src="*{icon}" style="max-height: 30px;"/>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">徽章类型:</label>
<div class="col-sm-8">
<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 == 3}">特殊徽章</div>
</div>
</div>
<div class="form-group" th:style="${badge.type == 2 ? 'display:block;' : 'display:none;'}">
<label class="col-sm-3 control-label">关联演出:</label>
<div class="col-sm-8">
<div class="form-control-static" th:text="*{performanceId}"></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">状态:</label>
<div class="col-sm-8">
<div class="form-control-static" th:if="*{displayStatus == 1}">已发布</div>
<div class="form-control-static" th:if="*{displayStatus == 0}">下架</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序:</label>
<div class="col-sm-8">
<div class="form-control-static" th:text="*{sort}"></div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">分享文案:</label>
<div class="col-sm-8">
<div class="form-control-static" th:text="*{shareText}"></div>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
$(function() {
// 隐藏底部的确认按钮,只保留关闭按钮
var index = parent.layer.getFrameIndex(window.name);
if (index) {
var $layero = parent.$("#layui-layer" + index);
$layero.find(".layui-layer-btn0").hide(); // 隐藏确认按钮
}
});
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('修改徽章')" />
<th:block th:include="include :: bootstrap-fileinput-css" />
</head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-badge-edit" th:object="${badge}">
<input name="badgeId" th:field="*{badgeId}" type="hidden">
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章名称:</label>
<div class="col-sm-8">
<input name="name" th:field="*{name}" class="form-control" type="text" maxlength="50" required placeholder="最多50个字">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">徽章副标题:</label>
<div class="col-sm-8">
<input name="subTitle" th:field="*{subTitle}" class="form-control" type="text" maxlength="20" placeholder="最多20个字">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章图标:</label>
<div class="col-sm-8">
<input type="hidden" name="icon" id="badgeIconUrl" th:value="*{icon}">
<div id="badgeIconPreviewWrap" class="m-b"
th:style="${badge.icon != null and (#strings.startsWith(badge.icon, 'http') or #strings.startsWith(badge.icon, '/'))} ? '' : 'display:none;'">
<img id="badgeIconPreview" alt="当前图标"
th:src="${badge.icon != null and (#strings.startsWith(badge.icon, 'http') or #strings.startsWith(badge.icon, '/'))} ? ${badge.icon} : ''"
style="max-height:96px;border-radius:4px;border:1px solid #eee;"/>
</div>
<p id="badgeIconLegacyHint" class="help-block"
th:if="${badge.icon != null and !(#strings.startsWith(badge.icon, 'http') or #strings.startsWith(badge.icon, '/'))}">
当前为 Emoji 文本:<strong th:text="${badge.icon}"></strong>,上传图片保存后将替换为图片地址。
</p>
<div class="file-loading">
<input id="fileinput-badge-icon" type="file" name="file" data-browse-on-zone-click="true">
</div>
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 支持 jpg、png、gif、webp,单张最大 5M;重新上传将覆盖地址。</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label is-required">徽章类型:</label>
<div class="col-sm-8">
<input type="hidden" name="type" th:value="*{type}"/>
<select class="form-control m-b" disabled>
<option th:selected="${badge.type == 1}" value="1">护照纪念徽章</option>
<option th:selected="${badge.type == 2}" value="2">演出纪念徽章</option>
</select>
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 徽章类型保存后不可修改</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">排序:</label>
<div class="col-sm-8">
<input name="sort" th:field="*{sort}" class="form-control" type="number" min="0" placeholder="数值越大越靠前">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">分享文案:</label>
<div class="col-sm-8">
<textarea name="shareText" th:field="*{shareText}" class="form-control" rows="3" maxlength="255" placeholder="请输入徽章分享文案"></textarea>
</div>
</div>
<div class="form-group" id="ticketTimesDiv" th:style="${badge.type == 2 ? 'display:block;' : 'display:none;'}">
<label class="col-sm-3 control-label is-required">关联演出:</label>
<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}">
<span class="help-block m-b-none"><i class="fa fa-info-circle"></i> 演出纪念徽章必填</span>
</div>
</div>
</form>
</div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: bootstrap-fileinput-js" />
<script type="text/javascript" th:inline="javascript">
var prefix = ctx + "adam/caomei/badge";
var platformUrl = /*[[${platformUrl}]]*/ '';
function typeChange(val) {
if (val == 2 || val == '2') {
$("#ticketTimesDiv").show();
$("#performanceId").prop("required", true);
} else {
$("#ticketTimesDiv").hide();
$("#performanceId").prop("required", false);
$("#performanceId").val("");
}
}
$(function () {
typeChange($('input[name="type"]').val());
$("#fileinput-badge-icon").fileinput({
'theme': 'explorer-fas',
'uploadUrl': platformUrl + "/platform/basicServices/alOss/upload/unsm",
"uploadExtraData": {
"pathName": "other",
"buckType": 1
},
autoReplace: true,
dropZoneTitle: "点击或拖拽上传徽章图标(可选,用于更换)",
maxFileCount: 1,
maxFileSize: 5120,
allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'],
msgSizeTooLarge: '文件 "{name}" ({size} KB) 超过了允许大小 {maxSize} KB,最大支持上传5M文件'
}).on("filebatchselected", function () {
$(this).fileinput("upload");
}).on("fileuploaded", function (event, data) {
if (!data.response || !data.response.data || !data.response.data.ossPath) {
$.modal.alertWarning((data.response && data.response.msg) ? data.response.msg : "图片上传失败");
return;
}
var fullUrl = "https://img.zhengzai.tv/" + data.response.data.ossPath;
$("#badgeIconUrl").val(fullUrl);
$("#badgeIconPreview").attr("src", fullUrl);
$("#badgeIconPreviewWrap").show();
$("#badgeIconLegacyHint").hide();
});
});
$("#form-badge-edit").validate({
focusCleanup: true
});
function submitHandler() {
if (!$("#badgeIconUrl").val()) {
$.modal.msgWarning("请先上传徽章图标(若为历史 Emoji 数据,请上传一张图片作为图标)");
return;
}
if ($.validate.form()) {
$.operate.save(prefix + "/edit", $('#form-badge-edit').serialize());
}
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('草莓护照徽章列表')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
徽章名称:<input type="text" name="name"/>
</li>
<li>
徽章类型:<select name="type" th:with="type=${@dict.getType('adam_caomei_badge_type')}">
<option value="">所有</option>
<option value="1">护照纪念徽章</option>
<option value="2">演出纪念徽章</option>
<!-- <option value="3">特殊徽章</option> -->
</select>
</li>
<li>
状态:<select name="displayStatus">
<option value="">所有</option>
<option value="0">下架</option>
<option value="1">已发布</option>
</select>
</li>
<li class="select-time">
<label>添加时间:</label>
<input type="text" class="time-input" id="badgeCreatedBegin" placeholder="开始日期" name="createdAtBegin"/>
<span> - </span>
<input type="text" class="time-input" id="badgeCreatedEnd" placeholder="结束日期" name="createdAtEnd"/>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="btn-group-sm" id="toolbar" role="group">
<a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="adam:caomei:badge:add">
<i class="fa fa-plus"></i> 新增
</a>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var editFlag = [[${@permission.hasPermi('adam:caomei:badge:edit')}]];
var prefix = ctx + "adam/caomei/badge";
$(function() {
var options = {
url: prefix + "/list",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
modalName: "草莓护照徽章",
columns: [{
checkbox: true
},
{
field: 'mid',
title: '主键ID',
visible: false
},
{
field: 'badgeId',
title: '徽章ID'
},
{
field: 'icon',
title: '图标',
formatter: function(value, row, index) {
// 如果是图片链接,可以用 $.table.imageView(value)
// 如果是 emoji,直接返回
if(value && (value.startsWith('http') || value.startsWith('/'))) {
return $.table.imageView(value);
}
return '<span style="font-size: 24px;">' + value + '</span>';
}
},
{
field: 'name',
title: '名称'
},
{
field: 'subTitle',
title: '副标题',
formatter: function(value) {
return value ? value : '-';
}
},
{
field: 'type',
title: '类型',
formatter: function(value, row, index) {
if (value == 1) return '<span class="badge badge-info">护照纪念</span>';
if (value == 2) return '<span class="badge badge-primary">演出纪念</span>';
if (value == 3) return '<span class="badge badge-warning">特殊徽章</span>';
return value;
}
},
{
field: 'createdAt',
title: '添加时间',
formatter: function(value, row, index) {
if (!value) return '-';
return value.length >= 10 ? value.substring(0, 10) : value;
}
},
{
field: 'claimedCount',
title: '领取人数'
},
{
field: 'sort',
title: '排序(越大越靠前)'
},
{
field: 'displayStatus',
title: '状态',
align: 'center',
formatter: function (value, row, index) {
if (value == 1) {
return '<span class="badge badge-primary" style="background-color: #e6f3ff; color: #1890ff; padding: 5px 10px; border-radius: 4px;">已发布</span>';
} else {
return '<span class="badge badge-default" style="background-color: #f5f5f5; color: #999; padding: 5px 10px; border-radius: 4px;">下架</span>';
}
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
// 详情按钮
actions.push('<a class="btn btn-info btn-sm" style="margin-right: 5px;" href="javascript:void(0)" onclick="detail(\'' + row.badgeId + '\')"><i class="fa fa-eye"></i>详情</a>');
// 仅下架状态可修改(编辑)
if (row.displayStatus == 0) {
actions.push('<a class="btn btn-success btn-sm ' + editFlag + '" style="margin-right: 5px;" href="javascript:void(0)" onclick="$.operate.edit(\'' + row.badgeId + '\')"><i class="fa fa-edit"></i>编辑</a>');
}
// 下架/发布按钮
if (row.displayStatus == 1) {
actions.push('<a class="btn btn-danger btn-sm" style="margin-right: 5px;" href="javascript:void(0)" onclick="disable(\'' + row.badgeId + '\')"><i class="fa fa-arrow-down"></i>下架</a>');
} else {
actions.push('<a class="btn btn-primary btn-sm" style="margin-right: 5px;" href="javascript:void(0)" onclick="enable(\'' + row.badgeId + '\')"><i class="fa fa-upload"></i>发布</a>');
}
// 领取用户按钮
actions.push('<a class="btn btn-warning btn-sm" href="javascript:void(0)" onclick="claimedUsers(\'' + row.badgeId + '\')"><i class="fa fa-users"></i>领取用户</a>');
return actions.join('');
}
}]
};
$.table.init(options);
});
/* 徽章下架 */
function disable(badgeId) {
$.modal.confirm("确认要下架该徽章吗?", function() {
$.operate.post(prefix + "/changeStatus", { "badgeId": badgeId, "displayStatus": 0 });
});
}
/* 徽章发布 */
function enable(badgeId) {
$.modal.confirm("确认要发布该徽章吗?", function() {
$.operate.post(prefix + "/changeStatus", { "badgeId": badgeId, "displayStatus": 1 });
});
}
/* 详情 */
function detail(badgeId) {
$.modal.open("徽章详情", prefix + "/detail/" + badgeId);
}
/* 领取用户 */
function claimedUsers(badgeId) {
if (!badgeId) {
$.modal.msgError("未找到徽章ID");
return;
}
$.modal.openOptions({
title: "领取用户",
url: prefix + "/claimUsers/" + badgeId,
skin: 'layui-layer-gray',
btn: ['关闭'],
yes: function (index, layero) {
layer.close(index);
}
});
}
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('补签详情')" />
</head>
<body class="gray-bg">
<div class="container-div" th:if="${detail != null}">
<div class="row">
<div class="col-sm-12">
<div class="ibox">
<div class="ibox-title"><h5>申请信息</h5></div>
<div class="ibox-content">
<table class="table table-bordered table-striped">
<tbody>
<tr><td class="col-sm-2">申请ID</td><td th:text="${detail.applyRecordId}">-</td></tr>
<tr><td>申请账号</td><td th:text="${detail.userId}">-</td></tr>
<tr><td>用户名</td><td th:text="${detail.nickname != null && detail.nickname != '' ? detail.nickname : '-'}">-</td></tr>
<tr><td>姓名</td><td th:text="${detail.realName != null && detail.realName != '' ? detail.realName : '-'}">-</td></tr>
<tr><td>证件号</td><td th:text="${detail.idCard != null && detail.idCard != '' ? detail.idCard : '-'}">-</td></tr>
<tr><td>申请徽章</td><td th:text="${detail.badgeName != null && detail.badgeName != '' ? detail.badgeName : '-'}">-</td></tr>
<tr><td>关联演出</td><td th:text="${detail.performanceName != null && detail.performanceName != '' ? detail.performanceName : '-'}">-</td></tr>
<tr>
<td>申请附件</td>
<td>
<span th:if="${detail.proofImageUrls == null or detail.proofImageUrls.isEmpty()}">-</span>
<a th:each="u : ${detail.proofImageUrls}" th:href="${u}" target="_blank" style="display:inline-block;margin:0 8px 8px 0;">
<img th:src="${u}" style="max-height: 90px; border-radius: 4px;"/>
</a>
</td>
</tr>
<tr>
<td>审核状态</td>
<td>
<span th:if="${detail.auditStatus == 0}" class="label label-warning">待审核</span>
<span th:if="${detail.auditStatus == 1}" class="label label-success">已通过</span>
<span th:if="${detail.auditStatus == 2}" class="label label-danger">已驳回</span>
</td>
</tr>
<tr><td>申请时间</td><td th:text="${detail.createdAt != null ? #dates.format(detail.createdAt, 'yyyy-MM-dd HH:mm:ss') : '-'}">-</td></tr>
<tr><td>审核时间</td><td th:text="${detail.updatedAt != null ? #dates.format(detail.updatedAt, 'yyyy-MM-dd HH:mm:ss') : '-'}">-</td></tr>
<tr th:if="${detail.auditStatus == 2}">
<td>驳回理由</td>
<td th:text="${detail.rejectReason != null && detail.rejectReason != '' ? detail.rejectReason : '-'}">-</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="container-div" th:if="${detail == null}">
<div class="alert alert-warning">未找到申请信息</div>
</div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
$(function() {
// 详情弹窗无需确认按钮,仅保留关闭
var index = parent.layer.getFrameIndex(window.name);
if (index) {
var $layero = parent.$("#layui-layer" + index);
$layero.find(".layui-layer-btn0").hide();
}
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('补签审核')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
用户名/姓名:<input type="text" name="userName" placeholder="昵称或姓名"/>
</li>
<li>
申请状态:
<select name="auditStatus">
<option value="">全部</option>
<option value="0">待审核</option>
<option value="1">已通过</option>
<option value="2">已驳回</option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;筛选</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var detailFlag = [[${@permission.hasPermi('adam:caomei:badgeApplyAudit:list')}]];
var passFlag = [[${@permission.hasPermi('adam:caomei:badgeApplyAudit:edit')}]];
var rejectFlag = [[${@permission.hasPermi('adam:caomei:badgeApplyAudit:edit')}]];
var prefix = ctx + "adam/caomei/badgeApplyAudit";
$(function() {
var options = {
url: prefix + "/list",
formId: "formId",
modalName: "补签审核",
columns: [
{ field: 'applyRecordId', title: '申请ID' },
{ field: 'userId', title: '申请账号' },
{ field: 'realName', title: '姓名', formatter: function(v) { return v || '-'; } },
{ field: 'idCard', title: '证件号', formatter: function(v) { return v || '-'; } },
{ field: 'badgeName', title: '申请徽章', formatter: function(v) { return v || '-'; } },
{ field: 'performanceName', title: '关联演出', formatter: function(v) { return v || '-'; } },
{
field: 'proofImageUrl',
title: '申请附件',
align: 'center',
formatter: function(value, row) {
var urls = row.proofImageUrls;
if (!urls || urls.length === 0) {
if (!value) return '-';
urls = [value];
}
var html = [];
for (var i = 0; i < urls.length; i++) {
var u = urls[i];
if (!u) continue;
html.push('<a href="' + u + '" target="_blank" title="查看原图"><img src="' + u + '" style="height:32px;max-width:60px;margin:0 2px;cursor:pointer;border-radius:3px;"/></a>');
}
return html.length ? html.join('') : '-';
}
},
{
field: 'auditStatus',
title: '审核状态',
align: 'center',
formatter: function(value) {
if (value === 0) return '<span class="label label-warning">待审核</span>';
if (value === 1) return '<span class="label label-success">已通过</span>';
if (value === 2) return '<span class="label label-danger">已驳回</span>';
return '-';
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row) {
var actions = [];
actions.push('<a class="btn btn-info btn-xs ' + detailFlag + '" href="javascript:void(0)" onclick=\'detail(' + JSON.stringify(row.applyRecordId) + ')\'><i class="fa fa-eye"></i>详情</a> ');
if (row.auditStatus === 0) {
actions.push('<a class="btn btn-success btn-xs ' + passFlag + '" href="javascript:void(0)" onclick=\'passAudit(' + JSON.stringify(row.applyRecordId) + ')\'><i class="fa fa-check"></i>通过</a> ');
actions.push('<a class="btn btn-danger btn-xs ' + rejectFlag + '" href="javascript:void(0)" onclick=\'rejectAudit(' + JSON.stringify(row.applyRecordId) + ')\'><i class="fa fa-close"></i>驳回</a>');
}
return actions.join('');
}
}
]
};
$.table.init(options);
});
function detail(applyRecordId) {
$.modal.open("补签详情", prefix + "/detail/" + encodeURIComponent(applyRecordId));
}
function passAudit(applyRecordId) {
$.modal.confirm("确认审核通过并为用户发放对应徽章吗?", function() {
$.operate.post(prefix + "/pass", { applyRecordId: applyRecordId });
});
}
function rejectAudit(applyRecordId) {
layer.prompt({
title: "请输入驳回理由",
formType: 2,
maxlength: 255
}, function(value, index) {
layer.close(index);
var reason = $.trim(value);
if (!reason) {
$.modal.msgError("驳回理由不能为空");
return;
}
$.operate.post(prefix + "/reject", { applyRecordId: applyRecordId, rejectReason: reason });
});
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="include :: header('护照详情')" />
</head>
<body class="gray-bg">
<div class="container-div" th:if="${detail != null}">
<div class="row">
<div class="col-sm-12">
<div class="ibox">
<div class="ibox-title"><h5>护照信息</h5></div>
<div class="ibox-content">
<table class="table table-bordered table-striped">
<tbody>
<tr><td class="col-sm-2">护照编号</td><td th:text="${detail.passportNo}">-</td></tr>
<tr><td>绑定状态</td>
<td>
<span th:if="${detail.status == 1}" class="label label-info">已绑定</span>
<span th:if="${detail.status == 0}" class="label label-default">未绑定</span>
<span th:if="${detail.status == 2}" class="label label-warning">已作废</span>
</td>
</tr>
<tr><td>批次号</td><td th:text="${detail.batchNo != null ? detail.batchNo : '-'}">-</td></tr>
<tr><td>首次绑定时间</td><td th:text="${detail.boundAt != null ? #dates.format(detail.boundAt, 'yyyy-MM-dd HH:mm:ss') : '-'}">-</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-12" th:if="${detail.userId != null and detail.userId != ''}">
<div class="ibox">
<div class="ibox-title"><h5>关联用户信息</h5></div>
<div class="ibox-content">
<table class="table table-bordered table-striped">
<tbody>
<tr><td class="col-sm-2">用户ID</td><td th:text="${detail.userId}">-</td></tr>
<tr><td>昵称</td><td th:text="${detail.nickname}">-</td></tr>
<tr><td>手机号</td><td th:text="${detail.mobile}">-</td></tr>
<tr><td>真实姓名</td><td th:text="${detail.realName}">-</td></tr>
<tr><td>证件号</td><td th:text="${detail.idCard}">-</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-12" th:if="${detail.userId == null or detail.userId == ''}">
<div class="alert alert-info">当前护照未绑定用户</div>
</div>
<div class="col-sm-12" th:if="${detail.badges != null and !#lists.isEmpty(detail.badges)}">
<div class="ibox">
<div class="ibox-title"><h5>已领取徽章</h5></div>
<div class="ibox-content">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>徽章名称</th>
<th>图标</th>
<th>领取时间</th>
<th>获取途径</th>
</tr>
</thead>
<tbody>
<tr th:each="b : ${detail.badges}">
<td th:text="${b.badgeName}">-</td>
<td>
<span th:if="${b.icon != null and (b.icon.startsWith('http') or b.icon.startsWith('/'))}">
<img th:src="${b.icon}" style="max-height:28px;"/>
</span>
<span th:if="${b.icon != null and !(b.icon.startsWith('http') or b.icon.startsWith('/'))}" th:text="${b.icon}" style="font-size:22px;"></span>
</td>
<td th:text="${b.claimedAt != null ? #dates.format(b.claimedAt, 'yyyy-MM-dd HH:mm:ss') : '-'}">-</td>
<td>
<span th:if="${b.source == 1}">绑定护照自动发放</span>
<span th:if="${b.source == 2}">购票自动发放</span>
<span th:if="${b.source == 3}">补签审核通过</span>
<span th:if="${b.source == 4}">现场管理员手动发放</span>
<span th:if="${b.source == null or b.source &lt; 1 or b.source &gt; 4}">未知</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="container-div" th:if="${detail == null}">
<div class="alert alert-warning">未找到护照信息</div>
</div>
<th:block th:include="include :: footer" />
</body>
</html>
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('护照管理')" />
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
<label>护照编号:</label>
<input type="text" name="passportNo" placeholder="模糊匹配"/>
</li>
<li>
<label>绑定状态:</label>
<select name="bindStatus">
<option value="">全部</option>
<option value="1">已绑定</option>
<option value="0">未绑定</option>
<option value="2">已作废</option>
</select>
</li>
<li>
<label>用户昵称/姓名:</label>
<input type="text" name="userName" placeholder="昵称或真实姓名"/>
</li>
<li>
<label>手机号:</label>
<input type="text" name="mobile" placeholder="完整或片段"/>
</li>
<li>
<label>证件号:</label>
<input type="text" name="idCard" placeholder="完整或片段"/>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer" />
<script th:inline="javascript">
var unbindFlag = [[${@permission.hasPermi('adam:caomei:passport:edit')}]];
var detailFlag = [[${@permission.hasPermi('adam:caomei:passport:list')}]];
var prefix = ctx + "adam/caomei/passport";
$(function() {
var options = {
url: prefix + "/list",
formId: "formId",
modalName: "护照",
columns: [
{ field: 'mid', title: '主键', visible: false },
{ field: 'passportNo', title: '护照ID' },
{
field: 'nickname',
title: '关联用户',
formatter: function(value, row, index) {
if (!value && row.bindStatus !== 1) {
return '<span class="text-muted">未绑定用户</span>';
}
return value || '-';
}
},
{ field: 'realName', title: '姓名', formatter: function(v) { return v || '-'; } },
{ field: 'mobile', title: '手机号', formatter: function(v) { return v || '-'; } },
{ field: 'idCard', title: '证件号', formatter: function(v) { return v || '-'; } },
{
field: 'bindStatus',
title: '绑定状态',
align: 'center',
formatter: function(value) {
if (value === 1) {
return '<span class="label label-info">已绑定</span>';
}
if (value === 0) {
return '<span class="label label-default">未绑定</span>';
}
if (value === 2) {
return '<span class="label label-warning">已作废</span>';
}
return value;
}
},
{ field: 'claimedBadgeCount', title: '已领取徽章数', align: 'center' },
{
title: '操作',
align: 'center',
formatter: function(value, row) {
var actions = [];
actions.push('<a class="btn btn-info btn-xs ' + detailFlag + '" href="javascript:void(0)" onclick=\'openDetail(' + JSON.stringify(row.passportNo) + ')\'><i class="fa fa-eye"></i>查看详情</a> ');
if (row.bindStatus === 1) {
actions.push('<a class="btn btn-warning btn-xs ' + unbindFlag + '" href="javascript:void(0)" onclick=\'unbind(' + JSON.stringify(row.passportNo) + ')\'><i class="fa fa-unlink"></i>解绑用户</a>');
}
return actions.join('');
}
}
]
};
$.table.init(options);
setTimeout(function() {
renderBoundPassportCountToolbar();
loadBoundPassportCount();
}, 0);
});
function renderBoundPassportCountToolbar() {
if ($("#boundPassportCountBox").length > 0) {
return;
}
var html = '' +
'<div id="boundPassportCountBox" class="pull-left" style="margin-top: 10px; margin-right: 8px;">' +
' <span style="display: inline-block; line-height: 30px; margin-right: 6px;">已绑定护照数量:<strong id="boundPassportCount">加载中...</strong></span>' +
' <button type="button" class="btn btn-default btn-outline" onclick="loadBoundPassportCount()" title="刷新已绑定护照数量">' +
' <i class="fa fa-refresh"></i>' +
' </button>' +
'</div>';
var $toolbar = $(".fixed-table-toolbar").first();
if ($toolbar.length > 0) {
$toolbar.prepend(html);
}
}
function loadBoundPassportCount() {
$.get(prefix + "/bound/count", function(res) {
if (res && res.code === 0) {
$("#boundPassportCount").text(res.data || 0);
} else {
$("#boundPassportCount").text("-");
}
});
}
function openDetail(passportNo) {
$.modal.openTab("护照详情", prefix + "/detail/" + encodeURIComponent(passportNo));
}
function unbind(passportNo) {
$.modal.confirm("解绑后该护照将变为未绑定状态,用户需重新绑定。确认解绑?", function() {
$.operate.post(prefix + "/unbind", { passportNo: passportNo });
});
}
</script>
</body>
</html>
package com.liquidnet.client.admin.zhengzai.adam.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeClaimUserSearchParam;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeClaimUserVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeVo;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
/**
* 草莓护照-徽章管理
*/
public interface IAdamCaomeiBadgeAdminService extends IService<AdamCaomeiBadge> {
/**
* 徽章管理(每个徽章领取人数)
* @param param
* @return
*/
PageInfo<AdamCaomeiBadgeVo> listWithClaimedCount(AdamCaomeiBadgeSearchParam param);
/**
* 徽章领取用户列表
* @param param
* @return
*/
PageInfo<AdamCaomeiBadgeClaimUserVo> listClaimUsers(AdamCaomeiBadgeClaimUserSearchParam param);
/**
* 清除用户端「上架草莓徽章列表」Redis 缓存,与 adam 侧 {@code AdamRdmService#delPublishedCaomeiBadges} 一致。
* 后台新增/编辑/上下架成功后应调用,避免用户端仍读到旧列表。
*/
void delPublishedCaomeiBadges();
/**
* kylin 演出是否存在(performances_id)
*/
boolean kylinPerformanceExists(String performancesId);
/**
* 是否存在与名称相同的其他徽章(trim 后精确匹配)
*
* @param name 徽章名称
* @param excludeBadgeId 编辑时排除当前徽章 ID;新增传 null 或空
*/
boolean existsOtherBadgeWithSameName(String name, String excludeBadgeId);
}
package com.liquidnet.client.admin.zhengzai.adam.service;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyAuditSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyAuditDetailVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyAuditVo;
public interface IAdamCaomeiBadgeApplyAuditAdminService {
PageInfo<AdamCaomeiBadgeApplyAuditVo> listApplyAudits(AdamCaomeiBadgeApplyAuditSearchParam param);
AdamCaomeiBadgeApplyAuditDetailVo getApplyAuditDetail(String applyRecordId);
boolean passApplyAudit(String applyRecordId);
boolean rejectApplyAudit(String applyRecordId, String rejectReason);
}
package com.liquidnet.client.admin.zhengzai.adam.service;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.adam.dto.param.AdamCaomeiPassportSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportDetailVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportListVo;
/**
* 草莓护照-护照管理
*/
public interface IAdamCaomeiPassportAdminService {
PageInfo<AdamCaomeiPassportListVo> listPassports(AdamCaomeiPassportSearchParam param);
int countBoundPassports();
AdamCaomeiPassportDetailVo getPassportDetail(String passportNo);
boolean unbindPassport(String passportNo);
}
package com.liquidnet.client.admin.zhengzai.adam.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiBadgeAdminService;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimCountDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimUserDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeClaimUserSearchParam;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeClaimUserVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeVo;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.kylin.entity.KylinPerformances;
import com.liquidnet.service.kylin.mapper.KylinPerformancesMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AdamCaomeiBadgeAdminServiceImpl extends ServiceImpl<AdamCaomeiBadgeMapper, AdamCaomeiBadge> implements IAdamCaomeiBadgeAdminService {
@Autowired
private RedisDataSourceUtil redisDataSourceUtil;
@Autowired
private KylinPerformancesMapper kylinPerformancesMapper;
@Override
public void delPublishedCaomeiBadges() {
redisDataSourceUtil.getRedisAdamUtil().del(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED);
}
@Override
public boolean save(AdamCaomeiBadge entity) {
boolean ok = super.save(entity);
if (ok) {
delPublishedCaomeiBadges();
}
return ok;
}
@Override
public boolean updateById(AdamCaomeiBadge entity) {
boolean ok = super.updateById(entity);
if (ok) {
delPublishedCaomeiBadges();
}
return ok;
}
@Override
public boolean kylinPerformanceExists(String performancesId) {
String id = StringUtils.trimToEmpty(performancesId);
if (StringUtils.isBlank(id)) {
return false;
}
int c = kylinPerformancesMapper.selectCount(
Wrappers.lambdaQuery(KylinPerformances.class).eq(KylinPerformances::getPerformancesId, id)
);
return c > 0;
}
@Override
public boolean existsOtherBadgeWithSameName(String name, String excludeBadgeId) {
String n = StringUtils.trimToEmpty(name);
if (StringUtils.isBlank(n)) {
return false;
}
LambdaQueryWrapper<AdamCaomeiBadge> w = Wrappers.lambdaQuery(AdamCaomeiBadge.class).eq(AdamCaomeiBadge::getName, n);
if (StringUtils.isNotBlank(excludeBadgeId)) {
w.ne(AdamCaomeiBadge::getBadgeId, excludeBadgeId.trim());
}
return this.count(w) > 0;
}
@Override
public PageInfo<AdamCaomeiBadgeVo> listWithClaimedCount(AdamCaomeiBadgeSearchParam param) {
LambdaQueryWrapper<AdamCaomeiBadge> queryWrapper = Wrappers.lambdaQuery(AdamCaomeiBadge.class);
if (StringUtils.isNotBlank(param.getName())) {
queryWrapper.like(AdamCaomeiBadge::getName, param.getName());
}
if (param.getType() != null) {
queryWrapper.eq(AdamCaomeiBadge::getType, param.getType());
}
if (param.getDisplayStatus() != null) {
queryWrapper.eq(AdamCaomeiBadge::getDisplayStatus, param.getDisplayStatus());
}
if (StringUtils.isNotBlank(param.getCreatedAtBegin())) {
try {
LocalDate d = LocalDate.parse(param.getCreatedAtBegin().trim());
queryWrapper.ge(AdamCaomeiBadge::getCreatedAt, Timestamp.valueOf(d.atStartOfDay()));
} catch (DateTimeParseException e) {
log.warn("invalid createdAtBegin: {}", param.getCreatedAtBegin());
}
}
if (StringUtils.isNotBlank(param.getCreatedAtEnd())) {
try {
LocalDate d = LocalDate.parse(param.getCreatedAtEnd().trim());
queryWrapper.le(AdamCaomeiBadge::getCreatedAt, Timestamp.valueOf(d.atTime(23, 59, 59)));
} catch (DateTimeParseException e) {
log.warn("invalid createdAtEnd: {}", param.getCreatedAtEnd());
}
}
queryWrapper.orderByDesc(AdamCaomeiBadge::getSort, AdamCaomeiBadge::getUpdatedAt);
List<AdamCaomeiBadge> badges = this.list(queryWrapper);
PageInfo<AdamCaomeiBadge> entityPage = new PageInfo<>(badges);
List<String> badgeIds = entityPage.getList().stream()
.map(AdamCaomeiBadge::getBadgeId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
// key: 徽章ID,value: 领取人数
Map<String, Integer> countMap = badgeIds.isEmpty() ? Collections.emptyMap() :
baseMapper.selectClaimedCountByBadgeIds(badgeIds).stream()
.collect(Collectors.toMap(AdamCaomeiBadgeClaimCountDto::getBadgeId, AdamCaomeiBadgeClaimCountDto::getClaimedCount, (a, b) -> a));
List<AdamCaomeiBadgeVo> voList = entityPage.getList().stream().map(item -> {
AdamCaomeiBadgeVo vo = new AdamCaomeiBadgeVo();
BeanUtils.copyProperties(item, vo);
vo.setClaimedCount(countMap.getOrDefault(item.getBadgeId(), 0));
return vo;
}).collect(Collectors.toList());
PageInfo<AdamCaomeiBadgeVo> voPage = new PageInfo<>(voList);
voPage.setTotal(entityPage.getTotal());
return voPage;
}
@Override
public PageInfo<AdamCaomeiBadgeClaimUserVo> listClaimUsers(AdamCaomeiBadgeClaimUserSearchParam param) {
if (param == null || StringUtils.isBlank(param.getBadgeId())) {
return new PageInfo<>(Collections.emptyList());
}
List<AdamCaomeiBadgeClaimUserDto> list = baseMapper.selectClaimUsersByBadgeId(param.getBadgeId());
PageInfo<AdamCaomeiBadgeClaimUserDto> dtoPage = new PageInfo<>(list);
List<AdamCaomeiBadgeClaimUserVo> voList = dtoPage.getList().stream().map(item -> {
AdamCaomeiBadgeClaimUserVo vo = new AdamCaomeiBadgeClaimUserVo();
BeanUtils.copyProperties(item, vo);
return vo;
}).collect(Collectors.toList());
PageInfo<AdamCaomeiBadgeClaimUserVo> voPage = new PageInfo<>(voList);
voPage.setTotal(dtoPage.getTotal());
return voPage;
}
}
package com.liquidnet.client.admin.zhengzai.adam.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiBadgeApplyAuditAdminService;
import com.liquidnet.client.admin.zhengzai.adam.util.CaomeiBadgeApplyProofUrls;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyAuditDetailDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyAuditDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyAuditSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyAuditDetailVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyAuditVo;
import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeApplyRecordMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class AdamCaomeiBadgeApplyAuditAdminServiceImpl implements IAdamCaomeiBadgeApplyAuditAdminService {
@Autowired
private AdamCaomeiBadgeApplyRecordMapper badgeApplyRecordMapper;
@Override
public PageInfo<AdamCaomeiBadgeApplyAuditVo> listApplyAudits(AdamCaomeiBadgeApplyAuditSearchParam param) {
if (param == null) {
param = new AdamCaomeiBadgeApplyAuditSearchParam();
}
List<AdamCaomeiBadgeApplyAuditDto> rows = badgeApplyRecordMapper.selectAuditList(
StringUtils.trimToEmpty(param.getUserName()),
param.getAuditStatus()
);
PageInfo<AdamCaomeiBadgeApplyAuditDto> dtoPage = new PageInfo<>(rows);
List<AdamCaomeiBadgeApplyAuditVo> voList = dtoPage.getList().stream().map(item -> {
AdamCaomeiBadgeApplyAuditVo vo = new AdamCaomeiBadgeApplyAuditVo();
BeanUtils.copyProperties(item, vo);
vo.setIdCard(maskIdCard(item.getIdCard()));
vo.setProofImageUrls(CaomeiBadgeApplyProofUrls.parse(item.getProofImageUrl()));
return vo;
}).collect(Collectors.toList());
PageInfo<AdamCaomeiBadgeApplyAuditVo> voPage = new PageInfo<>(voList);
voPage.setTotal(dtoPage.getTotal());
return voPage;
}
@Override
public AdamCaomeiBadgeApplyAuditDetailVo getApplyAuditDetail(String applyRecordId) {
if (StringUtils.isBlank(applyRecordId)) {
return null;
}
AdamCaomeiBadgeApplyAuditDetailDto detail = badgeApplyRecordMapper.selectAuditDetailByApplyRecordId(applyRecordId.trim());
if (detail == null) {
return null;
}
AdamCaomeiBadgeApplyAuditDetailVo vo = new AdamCaomeiBadgeApplyAuditDetailVo();
BeanUtils.copyProperties(detail, vo);
vo.setIdCard(maskIdCard(detail.getIdCard()));
vo.setProofImageUrls(CaomeiBadgeApplyProofUrls.parse(detail.getProofImageUrl()));
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean passApplyAudit(String applyRecordId) {
if (StringUtils.isBlank(applyRecordId)) {
return false;
}
AdamCaomeiBadgeApplyRecord record = badgeApplyRecordMapper.selectOne(
Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
.eq(AdamCaomeiBadgeApplyRecord::getApplyRecordId, applyRecordId.trim())
.last("limit 1")
);
if (record == null || record.getAuditStatus() == null || record.getAuditStatus() != 0) {
return false;
}
if (StringUtils.isBlank(record.getUserId()) || StringUtils.isBlank(record.getBadgeId())) {
return false;
}
int updated = badgeApplyRecordMapper.passAudit(applyRecordId.trim());
if (updated <= 0) {
return false;
}
return true;
}
@Override
public boolean rejectApplyAudit(String applyRecordId, String rejectReason) {
if (StringUtils.isBlank(applyRecordId) || StringUtils.isBlank(rejectReason)) {
return false;
}
return badgeApplyRecordMapper.rejectAudit(applyRecordId.trim(), rejectReason.trim()) > 0;
}
private static String maskIdCard(String idCard) {
if (StringUtils.isBlank(idCard)) {
// 未实名或无证件信息
return "";
}
if (idCard.length() > 8) {
return idCard.substring(0, 4) + "****" + idCard.substring(idCard.length() - 4);
}
return "****";
}
}
package com.liquidnet.client.admin.zhengzai.adam.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiPassportAdminService;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportListDto;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiPassportSearchParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportBadgeItemVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportDetailVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportListVo;
import com.liquidnet.service.adam.entity.AdamCaomeiPassport;
import com.liquidnet.service.adam.entity.AdamRealName;
import com.liquidnet.service.adam.entity.AdamUser;
import com.liquidnet.service.adam.entity.AdamUserInfo;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.mapper.AdamRealNameMapper;
import com.liquidnet.service.adam.mapper.AdamUserInfoMapper;
import com.liquidnet.service.adam.mapper.AdamUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AdamCaomeiPassportAdminServiceImpl implements IAdamCaomeiPassportAdminService {
@Autowired
private AdamCaomeiPassportMapper adamCaomeiPassportMapper;
@Autowired
private AdamUserMapper adamUserMapper;
@Autowired
private AdamUserInfoMapper adamUserInfoMapper;
@Autowired
private AdamRealNameMapper adamRealNameMapper;
@Autowired
private AdamCaomeiBadgeMapper adamCaomeiBadgeMapper;
@Autowired
private RedisDataSourceUtil redisDataSourceUtil;
@Override
public PageInfo<AdamCaomeiPassportListVo> listPassports(AdamCaomeiPassportSearchParam param) {
if (param == null) {
param = new AdamCaomeiPassportSearchParam();
}
List<AdamCaomeiPassportListDto> rows = adamCaomeiPassportMapper.selectPassportAdminList(
param.getPassportNo(),
param.getUserName(),
param.getMobile(),
param.getIdCard(),
param.getBindStatus()
);
PageInfo<AdamCaomeiPassportListDto> dtoPage = new PageInfo<>(rows);
List<AdamCaomeiPassportListVo> voList = dtoPage.getList().stream().map(d -> {
AdamCaomeiPassportListVo vo = new AdamCaomeiPassportListVo();
BeanUtils.copyProperties(d, vo);
return vo;
}).collect(Collectors.toList());
PageInfo<AdamCaomeiPassportListVo> voPage = new PageInfo<>(voList);
voPage.setTotal(dtoPage.getTotal());
return voPage;
}
@Override
public int countBoundPassports() {
return adamCaomeiPassportMapper.countBoundPassports();
}
@Override
public AdamCaomeiPassportDetailVo getPassportDetail(String passportNo) {
if (StringUtils.isBlank(passportNo)) {
return null;
}
AdamCaomeiPassport passport = adamCaomeiPassportMapper.selectOne(
Wrappers.lambdaQuery(AdamCaomeiPassport.class)
.eq(AdamCaomeiPassport::getPassportNo, passportNo.trim())
);
if (passport == null) {
return null;
}
AdamCaomeiPassportDetailVo vo = new AdamCaomeiPassportDetailVo();
vo.setMid(passport.getMid());
vo.setPassportNo(passport.getPassportNo());
vo.setStatus(passport.getStatus());
vo.setUserId(passport.getUserId());
vo.setBoundAt(passport.getBoundAt());
vo.setBatchNo(passport.getBatchNo());
String uid = passport.getUserId();
if (StringUtils.isBlank(uid)) {
vo.setBadges(Collections.emptyList());
return vo;
}
AdamUser user = adamUserMapper.selectOne(Wrappers.lambdaQuery(AdamUser.class).eq(AdamUser::getUid, uid));
AdamUserInfo userInfo = adamUserInfoMapper.selectOne(Wrappers.lambdaQuery(AdamUserInfo.class).eq(AdamUserInfo::getUid, uid));
vo.setNickname(userInfo != null ? StringUtils.defaultString(userInfo.getNickname()) : "");
vo.setMobile(user != null ? maskMobile(user.getMobile()) : "");
AdamRealName realName = adamRealNameMapper.selectOne(
Wrappers.lambdaQuery(AdamRealName.class)
.eq(AdamRealName::getUid, uid)
.eq(AdamRealName::getState, 1)
.orderByDesc(AdamRealName::getMid)
.last("limit 1")
);
if (realName != null) {
vo.setRealName(StringUtils.defaultString(realName.getName()));
vo.setIdCard(maskIdCard(realName.getIdCard()));
} else {
vo.setRealName("");
vo.setIdCard("");
}
List<AdamCaomeiPassportUserBadgeDto> badgeRows = adamCaomeiPassportMapper.selectUserBadgesByUid(uid);
vo.setBadges(badgeRows.stream().map(b -> {
AdamCaomeiPassportBadgeItemVo item = new AdamCaomeiPassportBadgeItemVo();
item.setBadgeId(b.getBadgeId());
item.setBadgeName(b.getBadgeName());
item.setIcon(b.getIcon());
item.setClaimedAt(b.getClaimedAt());
item.setSource(b.getSource());
return item;
}).collect(Collectors.toList()));
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean unbindPassport(String passportNo) {
if (StringUtils.isBlank(passportNo)) {
return false;
}
AdamCaomeiPassport passport = adamCaomeiPassportMapper.selectOne(
Wrappers.lambdaQuery(AdamCaomeiPassport.class)
.eq(AdamCaomeiPassport::getPassportNo, passportNo.trim())
);
if (passport == null) {
return false;
}
if (passport.getStatus() == null || passport.getStatus() != 1) {
return false;
}
String uid = passport.getUserId();
Date now = new Date();
passport.setStatus(0);
passport.setUserId("");
passport.setUnboundAt(now);
passport.setUpdatedAt(now);
boolean updated = adamCaomeiPassportMapper.updateById(passport) > 0;
if (!updated) {
return false;
}
if (StringUtils.isNotBlank(uid)) {
// 解绑后,清理该用户由“绑定护照”自动发放的护照纪念徽章
adamCaomeiBadgeMapper.deletePassportTypeBadgesByUid(uid);
// 清理用户草莓徽章缓存,避免前端短时间读到解绑前数据
redisDataSourceUtil.getRedisAdamUtil().del(AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid));
}
return true;
}
private static String maskMobile(String mobile) {
if (StringUtils.isBlank(mobile)) {
return "";
}
if (mobile.length() >= 11) {
return mobile.substring(0, 3) + "****" + mobile.substring(7);
}
return mobile;
}
private static String maskIdCard(String idCard) {
if (StringUtils.isBlank(idCard)) {
return "";
}
if (idCard.length() > 8) {
return idCard.substring(0, 4) + "****" + idCard.substring(idCard.length() - 4);
}
return "****";
}
}
package com.liquidnet.client.admin.zhengzai.adam.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* 补签申请附件 URL 解析(支持逗号/分号/换行分隔或 JSON 字符串数组)。
*/
public final class CaomeiBadgeApplyProofUrls {
private CaomeiBadgeApplyProofUrls() {
}
public static List<String> parse(String raw) {
if (StringUtils.isBlank(raw)) {
return Collections.emptyList();
}
String t = raw.trim();
if (t.startsWith("[") && t.endsWith("]")) {
try {
JSONArray arr = JSON.parseArray(t);
if (arr != null && !arr.isEmpty()) {
Set<String> seen = new LinkedHashSet<>();
for (int i = 0; i < arr.size(); i++) {
String u = StringUtils.trimToEmpty(arr.getString(i));
if (StringUtils.isNotBlank(u)) {
seen.add(u);
}
}
return new ArrayList<>(seen);
}
} catch (Exception ignored) {
// fall through to delimiter split
}
}
String[] parts = t.split("[,;\\n]+");
List<String> out = new ArrayList<>();
for (String p : parts) {
String u = StringUtils.trimToEmpty(p);
if (StringUtils.isNotBlank(u)) {
out.add(u);
}
}
return out;
}
}
......@@ -71,4 +71,7 @@ public class RedisKeyExpireConst {
* 收钱吧下单防重锁过期时间 (10秒)
*/
public static final long SQB_ORDER_LOCK_EXPIRE = 10;
// 已发布状态徽章过期时间:30天
public static final long CAOMEI_BADGE_PUBLISHED_EXPIRE = 30 * 24 * 60 * 60;
}
......@@ -87,6 +87,18 @@ spring:
max-wait: -1
max-idle: 8
min-idle: 0
kylin:
database: ${liquidnet.redis.kylin.database}
dbs: ${liquidnet.redis.kylin.dbs}
port: ${liquidnet.redis.kylin.port}
host: ${liquidnet.redis.kylin.host}
password: ${liquidnet.redis.kylin.password}
lettuce:
pool:
max-active: 16
max-wait: -1
max-idle: 8
min-idle: 4
database: 15
dbs: ${liquidnet.redis.adam.dbs}
port: ${liquidnet.redis.adam.port}
......
package com.liquidnet.service.adam.dto;
import lombok.Data;
import java.util.Date;
@Data
public class AdamCaomeiBadgeApplyAuditDetailDto {
private String applyRecordId;
private String userId;
private String nickname;
private String realName;
private String idCard;
private String badgeId;
private String badgeName;
private String performanceId;
private String performanceName;
private String proofImageUrl;
private Integer auditStatus;
private String rejectReason;
private Date createdAt;
private Date updatedAt;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
import java.util.Date;
@Data
public class AdamCaomeiBadgeApplyAuditDto {
private String applyRecordId;
private String userId;
private String nickname;
private String realName;
private String idCard;
private String badgeName;
private String performanceName;
private String proofImageUrl;
private Integer auditStatus;
private Date createdAt;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
import java.util.Date;
@Data
public class AdamCaomeiBadgeApplyRecordUserDto {
private String applyRecordId;
private String badgeName;
private Date applyTime;
private Integer auditStatus;
private String rejectReason;
private String proofImageUrl;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
@Data
public class AdamCaomeiBadgeClaimCountDto {
private String badgeId;
private Integer claimedCount;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
import java.util.Date;
@Data
public class AdamCaomeiBadgeClaimUserDto {
private String nickname;
private String mobile;
private Date claimedAt;
private Integer source;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
/**
* 护照管理列表行
*/
@Data
public class AdamCaomeiPassportListDto {
private Long mid;
private String passportNo;
private String nickname;
private String realName;
private String mobile;
private String idCard;
/** 0-未绑定 1-已绑定 2-已作废 */
private Integer bindStatus;
private Integer claimedBadgeCount;
}
package com.liquidnet.service.adam.dto;
import lombok.Data;
import java.util.Date;
@Data
public class AdamCaomeiPassportUserBadgeDto {
private String badgeId;
private String badgeName;
private String subTitle;
private String icon;
private String shareText;
private Integer type;
private String performanceId;
private Date claimedAt;
private Integer source;
}
package com.liquidnet.service.adam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 草莓护照-徽章配置表
*/
@Data
@TableName("adam_caomei_badge")
public class AdamCaomeiBadge implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
/**
* 徽章ID
*/
private String badgeId;
/**
* 徽章名称
*/
private String name;
/**
* 徽章副标题(最多10字)
*/
private String subTitle;
/**
* 徽章图标 (Emoji字符或图片URL)
*/
private String icon;
/**
* 徽章类型: 1-护照纪念徽章, 2-演出纪念徽章, 3-特殊徽章
*/
private Integer type;
/**
* 关联演出ID (仅演出纪念徽章必填,其他类型为空)
*/
private String performanceId;
/**
* 上架状态: 0-下架(默认), 1-已发布
*/
private Integer displayStatus;
/**
* 排序值,数值越大越靠前
*/
private Integer sort;
/**
* 徽章分享文案
*/
private String shareText;
/**
* 添加时间
*/
private Date createdAt;
/**
* 更新时间
*/
private Date updatedAt;
}
package com.liquidnet.service.adam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("adam_caomei_badge_apply_record")
public class AdamCaomeiBadgeApplyRecord implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String applyRecordId;
private String userId;
private String badgeId;
private String performanceId;
private String proofImageUrl;
private Integer auditStatus;
private String rejectReason;
private Date createdAt;
private Date updatedAt;
}
package com.liquidnet.service.adam.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 草莓护照-实体编号表
*/
@Data
@TableName("adam_caomei_passport")
public class AdamCaomeiPassport implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "mid", type = IdType.AUTO)
private Long mid;
private String passportNo;
/**
* 绑定状态: 0-未绑定, 1-已绑定, 2-已作废
*/
private Integer status;
private String userId;
private Date boundAt;
private Date unboundAt;
private String batchNo;
private String remark;
private Date createdAt;
private Date updatedAt;
}
package com.liquidnet.service.adam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyAuditDetailDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyAuditDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeApplyRecordUserDto;
import com.liquidnet.service.adam.entity.AdamCaomeiBadgeApplyRecord;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface AdamCaomeiBadgeApplyRecordMapper extends BaseMapper<AdamCaomeiBadgeApplyRecord> {
@Select({
"<script>",
"select ",
"ar.apply_record_id as applyRecordId,",
"ifnull(u.mobile, '') as userId,",
"ifnull(ui.nickname, '') as nickname,",
"ifnull(arn.name, '') as realName,",
"ifnull(arn.id_card, '') as idCard,",
"ifnull(b.name, '') as badgeName,",
"ifnull(kp.title, ar.performance_id) as performanceName,",
"ar.proof_image_url as proofImageUrl,",
"ar.audit_status as auditStatus,",
"ar.created_at as createdAt ",
"from adam_caomei_badge_apply_record ar ",
"left join adam_user u on ar.user_id = u.uid ",
"left join adam_user_info ui on ar.user_id = ui.uid ",
"left join (",
" select r.uid, r.name, r.id_card ",
" from adam_real_name r ",
" inner join (",
" select uid, max(mid) as max_mid from adam_real_name where state = 1 group by uid",
" ) rm on r.uid = rm.uid and r.mid = rm.max_mid and r.state = 1",
") arn on ar.user_id = arn.uid ",
"left join adam_caomei_badge b on ar.badge_id = b.badge_id ",
"left join kylin_performances kp on ar.performance_id = kp.performances_id ",
"where 1 = 1 ",
"<if test='userName != null and userName != \"\"'>",
"and (ui.nickname like concat('%', #{userName}, '%') or arn.name like concat('%', #{userName}, '%')) ",
"</if>",
"<if test='auditStatus != null'>",
"and ar.audit_status = #{auditStatus} ",
"</if>",
"order by case when ar.audit_status = 0 then 0 else 1 end asc, ar.created_at desc",
"</script>"
})
List<AdamCaomeiBadgeApplyAuditDto> selectAuditList(@Param("userName") String userName,
@Param("auditStatus") Integer auditStatus);
@Select({
"select ",
"ar.apply_record_id as applyRecordId,",
"ifnull(u.mobile, '') as userId,",
"ifnull(ui.nickname, '') as nickname,",
"ifnull(arn.name, '') as realName,",
"ifnull(arn.id_card, '') as idCard,",
"ar.badge_id as badgeId,",
"ifnull(b.name, '') as badgeName,",
"ar.performance_id as performanceId,",
"ifnull(kp.title, ar.performance_id) as performanceName,",
"ar.proof_image_url as proofImageUrl,",
"ar.audit_status as auditStatus,",
"ifnull(ar.reject_reason, '') as rejectReason,",
"ar.created_at as createdAt,",
"ar.updated_at as updatedAt ",
"from adam_caomei_badge_apply_record ar ",
"left join adam_user u on ar.user_id = u.uid ",
"left join adam_user_info ui on ar.user_id = ui.uid ",
"left join (",
" select r.uid, r.name, r.id_card ",
" from adam_real_name r ",
" inner join (select uid, max(mid) as max_mid from adam_real_name where state = 1 group by uid) rm ",
" on r.uid = rm.uid and r.mid = rm.max_mid and r.state = 1",
") arn on ar.user_id = arn.uid ",
"left join adam_caomei_badge b on ar.badge_id = b.badge_id ",
"left join kylin_performances kp on ar.performance_id = kp.performances_id ",
"where ar.apply_record_id = #{applyRecordId} ",
"limit 1"
})
AdamCaomeiBadgeApplyAuditDetailDto selectAuditDetailByApplyRecordId(@Param("applyRecordId") String applyRecordId);
@Update({
"update adam_caomei_badge_apply_record ",
"set audit_status = 1, reject_reason = null, updated_at = now() ",
"where apply_record_id = #{applyRecordId} and audit_status = 0"
})
int passAudit(@Param("applyRecordId") String applyRecordId);
@Update({
"update adam_caomei_badge_apply_record ",
"set audit_status = 2, reject_reason = #{rejectReason}, updated_at = now() ",
"where apply_record_id = #{applyRecordId} and audit_status = 0"
})
int rejectAudit(@Param("applyRecordId") String applyRecordId, @Param("rejectReason") String rejectReason);
@Insert({
"insert ignore into adam_caomei_user_badge (user_id, badge_id, source, created_at) ",
"values (#{userId}, #{badgeId}, 3, now())"
})
int issueBadgeIfAbsent(@Param("userId") String userId, @Param("badgeId") String badgeId);
@Select({
"select ",
"ar.apply_record_id as applyRecordId,",
"ifnull(b.name, '') as badgeName,",
"ar.created_at as applyTime,",
"ar.audit_status as auditStatus,",
"ifnull(ar.reject_reason, '') as rejectReason,",
"proof_image_url as proofImageUrl",
"from adam_caomei_badge_apply_record ar ",
"left join adam_caomei_badge b on ar.badge_id = b.badge_id ",
"where ar.user_id = #{uid} ",
"order by ar.created_at desc"
})
List<AdamCaomeiBadgeApplyRecordUserDto> selectUserApplyRecordsByUid(@Param("uid") String uid);
}
package com.liquidnet.service.adam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimCountDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimUserDto;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
import java.util.List;
public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> {
@Select({
"<script>",
"select badge_id as badgeId, count(1) as claimedCount",
"from adam_caomei_user_badge",
"where badge_id in",
"<foreach collection='badgeIds' item='badgeId' open='(' separator=',' close=')'>",
"#{badgeId}",
"</foreach>",
"group by badge_id",
"</script>"
})
List<AdamCaomeiBadgeClaimCountDto> selectClaimedCountByBadgeIds(@Param("badgeIds") List<String> badgeIds);
@Select({
"select ",
"ifnull(ui.nickname,'') as nickname,",
"case ",
"when u.mobile is null then '' ",
"when length(u.mobile) >= 11 then concat(substring(u.mobile,1,3),'****',substring(u.mobile,8,4)) ",
"else u.mobile end as mobile,",
"ub.created_at as claimedAt,",
"ub.source as source ",
"from adam_caomei_user_badge ub ",
"left join adam_user_info ui on ub.user_id = ui.uid ",
"left join adam_user u on ub.user_id = u.uid ",
"where ub.badge_id = #{badgeId} ",
"order by ub.created_at desc"
})
List<AdamCaomeiBadgeClaimUserDto> selectClaimUsersByBadgeId(@Param("badgeId") String badgeId);
@Delete({
"delete ub from adam_caomei_user_badge ub ",
"inner join adam_caomei_badge b on ub.badge_id = b.badge_id ",
"where ub.user_id = #{uid} and ub.source = 1 and b.type = 1"
})
int deletePassportTypeBadgesByUid(@Param("uid") String uid);
/**
* 绑定护照成功后,为已上架的「护照纪念徽章」(type=1) 批量发放 source=1(与解绑删除规则对称)
*/
@Insert({
"insert ignore into adam_caomei_user_badge (user_id, badge_id, source, created_at) ",
"select #{userId}, b.badge_id, 1, now() ",
"from adam_caomei_badge b ",
"where b.type = 1 and b.display_status = 1"
})
int insertPassportBindingBadgesForUser(@Param("userId") String userId);
@Insert({
"<script>",
"insert ignore into adam_caomei_user_badge (user_id, badge_id, source, created_at) values ",
"<foreach collection='badgeIds' item='badgeId' separator=','>",
"(#{userId}, #{badgeId}, #{source}, #{createdAt})",
"</foreach>",
"</script>"
})
int insertUserBadgesBatch(@Param("userId") String userId,
@Param("badgeIds") List<String> badgeIds,
@Param("source") Integer source,
@Param("createdAt") Date createdAt);
/**
* 根据身份证号查询用户已支付的演出ID列表
*/
@Select({
"select distinct performance_id ",
"from kylin_order_ticket_entities ",
"where enter_id_code = #{idCard} and is_payment = 1"
})
List<String> selectPaidPerformanceIdsByIdCard(@Param("idCard") String idCard);
@Select("select count(1) from adam_caomei_user_badge where user_id = #{userId} and badge_id = #{badgeId}")
int checkUserBadgeExists(@Param("userId") String userId, @Param("badgeId") String badgeId);
@Select({
"<script>",
"select badge_id from adam_caomei_user_badge",
"where user_id = #{userId} and badge_id in",
"<foreach collection='badgeIds' item='badgeId' open='(' separator=',' close=')'>",
"#{badgeId}",
"</foreach>",
"</script>"
})
List<String> selectClaimedBadgeIdsByUserAndBadgeIds(@Param("userId") String userId,
@Param("badgeIds") List<String> badgeIds);
@Insert({
"insert into adam_caomei_user_badge (user_id, badge_id, source, created_at) ",
"values (#{userId}, #{badgeId}, #{source}, now())"
})
int insertUserBadge(@Param("userId") String userId, @Param("badgeId") String badgeId, @Param("source") Integer source);
}
package com.liquidnet.service.adam.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportListDto;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.entity.AdamCaomeiPassport;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface AdamCaomeiPassportMapper extends BaseMapper<AdamCaomeiPassport> {
List<AdamCaomeiPassportListDto> selectPassportAdminList(@Param("passportNo") String passportNo,
@Param("userName") String userName,
@Param("mobile") String mobile,
@Param("idCard") String idCard,
@Param("bindStatus") Integer bindStatus);
@Select("select count(1) from adam_caomei_passport where status = 1")
int countBoundPassports();
List<AdamCaomeiPassportUserBadgeDto> selectUserBadgesByUid(@Param("uid") String uid);
/**
* 原子操作:仅当护照状态为 0(未绑定)时,更新为 1(已绑定)
*/
@Update("update adam_caomei_passport set status = 1, user_id = #{userId}, bound_at = now(), updated_at = now() where passport_no = #{passportNo} and status = 0")
int bindPassportAtomic(@Param("passportNo") String passportNo, @Param("userId") String userId);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper">
<select id="selectPassportAdminList" resultType="com.liquidnet.service.adam.dto.AdamCaomeiPassportListDto">
SELECT
p.mid AS mid,
p.passport_no AS passportNo,
IFNULL(ui.nickname, '') AS nickname,
IFNULL(arn.name, '') AS realName,
CASE
WHEN u.mobile IS NULL OR u.mobile = '' THEN ''
WHEN CHAR_LENGTH(u.mobile) &gt;= 11 THEN CONCAT(SUBSTRING(u.mobile, 1, 3), '****', SUBSTRING(u.mobile, 8, 4))
ELSE u.mobile
END AS mobile,
CASE
WHEN arn.id_card IS NULL OR arn.id_card = '' THEN ''
WHEN CHAR_LENGTH(arn.id_card) &gt; 8 THEN CONCAT(SUBSTRING(arn.id_card, 1, 4), '****', SUBSTRING(arn.id_card, CHAR_LENGTH(arn.id_card) - 3, 4))
ELSE '****'
END AS idCard,
p.status AS bindStatus,
IFNULL(bc.cnt, 0) AS claimedBadgeCount
FROM adam_caomei_passport p
LEFT JOIN adam_user u ON p.user_id = u.uid AND p.user_id &lt;&gt; ''
LEFT JOIN adam_user_info ui ON p.user_id = ui.uid
LEFT JOIN (
SELECT r.uid, r.name, r.id_card
FROM adam_real_name r
INNER JOIN (
SELECT uid, MAX(mid) AS max_mid
FROM adam_real_name
WHERE state = 1
GROUP BY uid
) rm ON r.uid = rm.uid AND r.mid = rm.max_mid AND r.state = 1
) arn ON p.user_id = arn.uid
LEFT JOIN (
SELECT user_id, COUNT(1) AS cnt
FROM adam_caomei_user_badge
GROUP BY user_id
) bc ON p.user_id = bc.user_id
<where>
<if test="passportNo != null and passportNo != ''">
AND p.passport_no LIKE CONCAT('%', #{passportNo}, '%')
</if>
<if test="userName != null and userName != ''">
AND (ui.nickname LIKE CONCAT('%', #{userName}, '%') OR arn.name LIKE CONCAT('%', #{userName}, '%'))
</if>
<if test="mobile != null and mobile != ''">
AND u.mobile LIKE CONCAT('%', #{mobile}, '%')
</if>
<if test="idCard != null and idCard != ''">
AND arn.id_card LIKE CONCAT('%', #{idCard}, '%')
</if>
<if test="bindStatus != null">
AND p.status = #{bindStatus}
</if>
</where>
<!-- 已绑定优先展示,避免仅按更新时间导致已绑定记录长期沉底 -->
ORDER BY (p.status = 1) DESC, p.updated_at DESC, p.mid DESC
</select>
<select id="selectUserBadgesByUid" resultType="com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto">
SELECT
ub.badge_id AS badgeId,
IFNULL(b.name, '') AS badgeName,
IFNULL(b.sub_title, '') AS subTitle,
IFNULL(b.icon, '') AS icon,
IFNULL(b.share_text, '') AS shareText,
IFNULL(b.type, 0) AS type,
IFNULL(b.performance_id, '') AS performanceId,
ub.created_at AS claimedAt,
ub.source AS source
FROM adam_caomei_user_badge ub
LEFT JOIN adam_caomei_badge b ON ub.badge_id = b.badge_id
WHERE ub.user_id = #{uid}
ORDER BY ub.created_at DESC
</select>
</mapper>
......@@ -23,6 +23,11 @@
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-web</artifactId>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-cache-redis</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.liquidnet</groupId>-->
<!-- <artifactId>liquidnet-common-cache-redisson</artifactId>-->
......@@ -38,11 +43,11 @@
<artifactId>liquidnet-service-adam-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.liquidnet</groupId>-->
<!-- <artifactId>liquidnet-service-kylin-api</artifactId>-->
<!-- <version>1.0-SNAPSHOT</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-service-kylin-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-easemob</artifactId>
......
package com.liquidnet.service.adam.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeApplyParam;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeClaimParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiBadgeApplyRecordUserVo;
import com.liquidnet.service.adam.service.IAdamCaomeiBadgeUserService;
import com.liquidnet.service.base.ResponseDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import javax.validation.Valid;
import java.util.List;
@ApiSupport(order = 10046)
@Api(tags = "草莓徽章(用户端)")
@Slf4j
@Validated
@RestController
@RequestMapping("caomei/badge")
public class AdamCaomeiBadgeUserController {
@Autowired
private IAdamCaomeiBadgeUserService adamCaomeiBadgeUserService;
@ApiOperationSupport(order = 1)
@ApiOperation("认领徽章")
@PostMapping("claim")
public ResponseDto<List<String>> claim(@Valid @RequestBody AdamCaomeiBadgeClaimParam param) {
String uid = CurrentUtil.getCurrentUid();
return adamCaomeiBadgeUserService.claimBadges(param.getBadgeIds(), uid);
}
@ApiOperationSupport(order = 2)
@ApiOperation("补签记录列表")
@GetMapping("apply/list")
public ResponseDto<List<AdamCaomeiBadgeApplyRecordUserVo>> applyList() {
String uid = CurrentUtil.getCurrentUid();
return adamCaomeiBadgeUserService.getApplyRecords(uid);
}
@ApiOperationSupport(order = 3)
@ApiOperation("发起补签申请(驳回后可重新上传)")
@PostMapping("apply")
public ResponseDto<String> apply(@Valid @RequestBody AdamCaomeiBadgeApplyParam param) {
String uid = CurrentUtil.getCurrentUid();
return adamCaomeiBadgeUserService.applyBadge(param, uid);
}
}
package com.liquidnet.service.adam.controller;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.service.adam.dto.param.AdamCaomeiPassportNoParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportHomeVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportUserClaimedBadgeVo;
import com.liquidnet.service.adam.service.IAdamCaomeiPassportUserService;
import com.liquidnet.service.base.ResponseDto;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@ApiSupport(order = 10045)
@Api(tags = "草莓护照(用户端)")
@Slf4j
@Validated
@RestController
@RequestMapping("caomei/passport")
public class AdamCaomeiPassportUserController {
@Autowired
private IAdamCaomeiPassportUserService adamCaomeiPassportUserService;
@ApiOperationSupport(order = 1)
@ApiOperation("绑定实体护照(内含·编号有效性、是否可绑等校验)")
@PostMapping("bind")
public ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bind(@Valid @RequestBody AdamCaomeiPassportNoParam param) {
return adamCaomeiPassportUserService.bindPassport(param.getPassportNo());
}
@ApiOperationSupport(order = 2)
@ApiOperation("护照首页聚合数据")
@GetMapping("home")
public ResponseDto<AdamCaomeiPassportHomeVo> home() {
return adamCaomeiPassportUserService.getPassportHome();
}
@ApiOperationSupport(order = 3)
@ApiOperation("校验当前用户是否已绑定护照")
@GetMapping("/check")
public ResponseDto<Boolean> checkBound() {
return adamCaomeiPassportUserService.checkPassportBound();
}
}
......@@ -12,6 +12,7 @@ import com.liquidnet.commons.lang.util.SensitizeUtil;
import com.liquidnet.service.adam.constant.AdamConst;
import com.liquidnet.service.adam.dto.AdamThirdPartParam;
import com.liquidnet.service.adam.dto.AdamUserInfoParam;
import com.liquidnet.service.adam.dto.param.AdamIdentityParam;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamUserInfoService;
......@@ -299,16 +300,15 @@ public class AdamUserController {
}
String currentUid = CurrentUtil.getCurrentUid();
AdamRealInfoVo realInfoVoByUid = adamRdmService.getRealInfoVoByUidPlain(currentUid);
// 已实名 && 三要素通过
if (null != realInfoVoByUid && realInfoVoByUid.getNode() == 3) {
// 用户输入信息与三要素通过的信息相符,则认证通过
if ((realInfoVoByUid.getName().concat(realInfoVoByUid.getIdCard())).equals(name.concat(idCard))) {
realInfoVoByUid.setName(SensitizeUtil.chineseName(realInfoVoByUid.getName()));
realInfoVoByUid.setIdCard(SensitizeUtil.custom(realInfoVoByUid.getIdCard(), 3, 2));
return ResponseDto.success(realInfoVoByUid);
} else {
return ResponseDto.failure(ErrorMapping.get("10113"));
}
// 已实名:无论二要素(node=2)还是三要素(node=3),都视为已完成实名,直接返回(避免重复实名/跨入口重复认证)
if (null != realInfoVoByUid && realInfoVoByUid.getState() != null && realInfoVoByUid.getState() == 1) {
realInfoVoByUid.setName(SensitizeUtil.chineseName(realInfoVoByUid.getName()));
realInfoVoByUid.setIdCard(SensitizeUtil.custom(realInfoVoByUid.getIdCard(), 3, 2));
return ResponseDto.success(realInfoVoByUid);
}
ResponseDto<AdamRealInfoVo> guard = guardIdCardNotBoundToOtherUid(currentUid, idCard);
if (guard != null) {
return guard;
}
AdamRealInfoVo vo = adamUserService.identityForUpsert(currentUid, name, idCard, (String) CurrentUtil.getTokenClaims().get(CurrentUtil.TOKEN_MOBILE), true);
vo.setName(SensitizeUtil.chineseName(vo.getName()));
......@@ -503,7 +503,8 @@ public class AdamUserController {
return ResponseDto.failure(ErrorMapping.get("10114"));
}
AdamRealInfoVo realInfoVoByUidPlain = adamRdmService.getRealInfoVoByUidPlain(uid);
if (null == realInfoVoByUidPlain || 3 != realInfoVoByUidPlain.getNode()) {
// 已实名:兼容二要素(node=2) 与 三要素(node=3),统一以 state=1 为准
if (null == realInfoVoByUidPlain || realInfoVoByUidPlain.getState() == null || realInfoVoByUidPlain.getState() != 1) {
return ResponseDto.failure(ErrorMapping.get("10115"));
}
AdamUserIdentityInfoVo userIdentityInfoVo = AdamUserIdentityInfoVo.getNew();
......@@ -535,6 +536,90 @@ public class AdamUserController {
return ResponseDto.success(adamRdmService.getUserInfoVoByUid(uid));
}
@ApiOperationSupport(order = 15)
@ApiOperation(value = "实名认证V2(二要素)", notes = "支持手动填写(name+idCard) 或 选择观演人(entersId)")
@PostMapping(value = {"identity/v2"})
public ResponseDto<AdamRealInfoVo> identityV2(@RequestBody AdamIdentityParam param) {
String currentUid = CurrentUtil.getCurrentUid();
// 1. 检查当前登录用户是否已实名
AdamRealInfoVo realInfoVo = adamRdmService.getRealInfoVoByUidPlain(currentUid);
if (realInfoVo != null && realInfoVo.getState() != null && realInfoVo.getState() == 1) {
// 已实名,直接返回
realInfoVo.setName(SensitizeUtil.chineseName(realInfoVo.getName()));
realInfoVo.setIdCard(SensitizeUtil.custom(realInfoVo.getIdCard(), 3, 2));
return ResponseDto.success(realInfoVo);
}
String name = param.getName();
String idCard = param.getIdCard();
// 2. 如果传了 entersId,则根据 entersId 获取观演人的姓名和身份证号
if (StringUtils.isNotBlank(param.getEntersId())) {
List<AdamEntersVo> entersList = adamRdmService.getEntersVoByUid(currentUid);
if (!CollectionUtils.isEmpty(entersList)) {
AdamEntersVo entersVo = entersList.stream()
.filter(e -> param.getEntersId().equals(e.getEntersId()))
.findFirst()
.orElse(null);
if (entersVo == null) {
return ResponseDto.failure(ErrorMapping.get("10105")); // 观演人不存在
}
// 只同步身份证类型的观演人信息 (type=1 大陆身份证)
if (entersVo.getType() == null || entersVo.getType() != 1) {
return ResponseDto.failure("10101", "仅支持大陆身份证类型的观演人进行实名认证");
}
name = entersVo.getName();
idCard = entersVo.getIdCard();
} else {
return ResponseDto.failure(ErrorMapping.get("10105")); // 观演人不存在
}
}
// 3. 校验姓名和身份证号不能为空
if (StringUtils.isBlank(name) || StringUtils.isBlank(idCard)) {
return ResponseDto.failure(ErrorMapping.get("10101")); // 姓名或身份证件号无效
}
// 4. 格式校验
if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_HANZI, name)) {
return ResponseDto.failure(ErrorMapping.get("10103"));
}
if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_ID_CARD_REF, idCard)) {
return ResponseDto.failure(ErrorMapping.get("10104"));
}
ResponseDto<AdamRealInfoVo> guard = guardIdCardNotBoundToOtherUid(currentUid, idCard);
if (guard != null) {
return guard;
}
AdamRealInfoVo vo = adamUserService.verifyTwoElements(currentUid, name, idCard);
if (null == vo) {
log.error("[identityV2] 二要素认证失败, param: {}", JsonUtils.toJson(param));
return ResponseDto.failure(ErrorMapping.get("10101"));
}
vo.setName(SensitizeUtil.chineseName(vo.getName()));
vo.setIdCard(SensitizeUtil.custom(vo.getIdCard(), 3, 2));
return ResponseDto.success(vo);
}
/**
* 同一个身份证号(state=1)只能绑定一个账号。
* 放在 controller 层:对所有实名入口统一拦截,返回明确错误码给前端。
*/
private ResponseDto<AdamRealInfoVo> guardIdCardNotBoundToOtherUid(String uid, String idCard) {
if (StringUtils.isBlank(uid) || StringUtils.isBlank(idCard)) {
return null;
}
String boundUid = adamRdmService.getRealNameBoundUidByIdCard(idCard);
if (StringUtils.isNotBlank(boundUid) && !uid.equals(boundUid)) {
return ResponseDto.failure(ErrorMapping.get("10614"));
}
return null;
}
/* ---------------------------- Internal Method ---------------------------- */
private static final String PHP_API_SMS_CODE_VALID = "/smsValidation";
......
package com.liquidnet.service.adam.service.impl;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.IDGenerator;
......@@ -204,7 +205,21 @@ public class AdamUserServiceImpl implements IAdamUserService {
} else if (thirdPartVo.getOpenId().equals(param.getOpenId())) {
userInfoVo = adamRdmService.getUserInfoVoByUid(uid);
} else {
return ResponseDto.failure(ErrorMapping.get("10010"));
// login/tpa 带手机号:仅 QQ 允许同一 uid 下 openId 变更时无感知换绑;其它平台仍走原校验
if (!"QQ".equals(param.getPlatform())) {
return ResponseDto.failure(ErrorMapping.get("10010"));
}
String existUidForOpenId = adamRdmService.getUidByPlatformOpenId(param.getPlatform(), param.getOpenId());
if (StringUtils.isNotEmpty(existUidForOpenId) && !existUidForOpenId.equals(uid)) {
return ResponseDto.failure(ErrorMapping.get("10007"));
}
userInfoVo = adamRdmService.getUserInfoVoByUid(uid);
if (null == userInfoVo || userInfoVo.getState() == 2) {
log.warn("Cancelled mobile:{}", param.getMobile());
return ResponseDto.failure(ErrorMapping.get("10024"));
}
this.unBindTpa(uid, param.getPlatform());
this.bindTpa(uid, param);
}
}
return ResponseDto.success(userInfoVo);
......@@ -445,6 +460,15 @@ public class AdamUserServiceImpl implements IAdamUserService {
// adamRdmService.setCertification(1, idCard, name);
// }
// adamRdmService.identityHandler1(uid, name, idCard);
// 优先校验身份证是否已绑定其他账号(需要优先于三方三要素校验提示)
if (StringUtils.isNotBlank(idCard)) {
String boundUid = adamRdmService.getRealNameBoundUidByIdCard(idCard);
if (StringUtils.isNotBlank(boundUid) && !boundUid.equals(uid)) {
ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10614");
throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
}
}
adamRdmService.identityHandler3(name, idCard, mobile);
AdamRealName realName = new AdamRealName();
......@@ -509,4 +533,32 @@ public class AdamUserServiceImpl implements IAdamUserService {
log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);
return vo;
}
@Override
public AdamRealInfoVo verifyTwoElements(String uid, String name, String idCard) {
try {
adamRdmService.identityHandler1(uid, name, idCard);
AdamRealName realName = new AdamRealName();
realName.setRealNameId(IDGenerator.nextSnowId() + "");
realName.setUid(uid);
realName.setType(1);
realName.setNode(2);
realName.setName(name);
realName.setIdCard(idCard);
realName.setState(1);
realName.setCreatedAt(LocalDateTime.now());
adamRealNameService.add(realName);
AdamRealInfoVo vo = AdamRealInfoVo.getNew().copy(realName);
long s = System.currentTimeMillis();
adamRdmService.setRealInfoVoByUid(uid, vo);
log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);
return vo;
}catch (Exception e) {
log.error("error", e);
return null;
}
}
}
......@@ -86,6 +86,23 @@
10504=\u4F1A\u5458\u8BA2\u5355\u56DE\u8C03\u5904\u7406\u5931\u8D25\uFF0C\u4F1A\u5458\u4EF7\u683C\u4FE1\u606F\u4E0D\u5B58\u5728
10505=\u4F1A\u5458\u8BA2\u5355\u56DE\u8C03\u5904\u7406\u5F02\u5E38
# \u8349\u8393\u62A4\u7167\uFF08C\u7AEF\u7ED1\u5B9A/\u9996\u9875\uFF09
10600=\u62A4\u7167\u7F16\u53F7\u65E0\u6548\u6216\u4E0D\u5B58\u5728
10601=\u8BE5\u62A4\u7167\u5DF2\u4F5C\u5E9F
10602=\u8BE5\u62A4\u7167\u5DF2\u88AB\u5176\u4ED6\u8D26\u53F7\u7ED1\u5B9A
10603=\u60A8\u5DF2\u7ED1\u5B9A\u5176\u4ED6\u62A4\u7167\uFF0C\u65E0\u6CD5\u91CD\u590D\u7ED1\u5B9A
10604=\u672A\u7ED1\u5B9A\u62A4\u7167
10605=\u62A4\u7167\u7ED1\u5B9A\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5
10606=\u8BE5\u62A4\u7167\u5DF2\u4E0E\u60A8\u7ED1\u5B9A\uFF0C\u65E0\u9700\u91CD\u590D\u64CD\u4F5C
10607=\u5FBD\u7AE0\u4E0D\u5B58\u5728\u6216\u672A\u4E0A\u67B6
10608=\u60A8\u5DF2\u9886\u53D6\u8FC7\u8BE5\u5FBD\u7AE0
10609=\u8BA4\u9886\u62A4\u7167\u7EAA\u5FF5\u5FBD\u7AE0\u9700\u5148\u7ED1\u5B9A\u62A4\u7167
10610=\u8BA4\u9886\u6F14\u51FA\u5FBD\u7AE0\u9700\u5148\u5B9E\u540D
10611=\u60A8\u6682\u65E0\u8D2D\u7968\u8BB0\u5F55\uFF0C\u8BF7\u4E0A\u4F20\u8BA2\u5355\u622A\u56FE
10612=\u7279\u6B8A\u5FBD\u7AE0\u4E0D\u53EF\u81EA\u52A9\u9886\u53D6
10613=\u672A\u77E5\u7684\u5FBD\u7AE0\u7C7B\u578B
10614=\u8BE5\u5B9E\u540D\u4FE1\u606F\u5DF2\u7ED1\u5B9A\u5176\u4ED6\u8D26\u53F7
......@@ -80,10 +80,16 @@ adam_user_mobile_locate.update_province=UPDATE adam_user_mobile_locate SET provi
adam_user_mobile_locate.close=UPDATE adam_user_mobile_locate SET `state`=2, updated_at=? WHERE uid=? AND `state`=1
# ----------------------------------------------------
# \u8349\u8393\u62A4\u7167-\u7528\u6237\u5FBD\u7AE0\u83B7\u5F97\u8BB0\u5F55 (adam_caomei_user_badge)
# \u7528\u6237\u7AEF claimBadge\uFF1A\u4E0E AdamCaomeiBadgeUserServiceImpl \u4E2D\u300C\u5148\u5199 Redis \u7528\u6237\u5FBD\u7AE0\u5217\u8868\u3001\u518D MQ \u5F02\u6B65\u843D\u5E93\u300D\u914D\u5957\uFF1B\u53C2\u6570\u987A\u5E8F user_id, badge_id, source\uFF1Bcreated_at \u7531\u5E93\u7AEF now() \u5199\u5165
adam_caomei_user_badge.add=INSERT INTO adam_caomei_user_badge (user_id, badge_id, source, created_at) VALUES (?,?,?,?)
# \u8349\u8393\u62A4\u7167-\u5FBD\u7AE0\u8865\u7B7E\u7533\u8BF7 (adam_caomei_badge_apply_record)
# \u7528\u6237\u7AEF applyBadge\uFF1A\u4E0E\u300C\u5148\u5199 Redis \u8865\u7B7E\u5217\u8868\u3001\u518D MQ \u5F02\u6B65\u843D\u5E93\u300D\u914D\u5957\uFF1B\u53C2\u6570\u987A\u5E8F apply_record_id, user_id, badge_id, performance_id, proof_image_url\uFF1Baudit_status=0\u3001reject_reason \u7A7A\u4E32\u3001\u65F6\u95F4\u7531 now() \u5199\u5165
adam_caomei_badge_apply_record.add=INSERT INTO adam_caomei_badge_apply_record (apply_record_id, user_id, badge_id, performance_id, proof_image_url, audit_status, reject_reason, created_at, updated_at) VALUES (?,?,?,?,?,0,'',now(),now())
# ----------------------------------------------------
# 补签自动通过:直接写入 audit_status=1
adam_caomei_badge_apply_record.add_passed=INSERT INTO adam_caomei_badge_apply_record (apply_record_id, user_id, badge_id, performance_id, proof_image_url, audit_status, reject_reason, created_at, updated_at) VALUES (?,?,?,?,?,1,'',now(),now())
# ----------------------------------------------------
candy_user_coupon.close=UPDATE candy_user_coupon SET state=2,updated_at=sysdate(),operator='close' WHERE uid=? AND state=1
......
......@@ -64,6 +64,11 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
GoblinCouponService goblinCouponService;
@Autowired
private GoblinSqbPerformanceGoodsMapper goblinSqbPerformanceGoodsMapper;
private static final String SELECT_GOODS_EXCLUDE_NAME = "护照专属";
private boolean isPassportExclusive(GoblinGoodsInfoVo vo) {
return null != vo && StringUtil.isNotBlank(vo.getName()) && vo.getName().contains(SELECT_GOODS_EXCLUDE_NAME);
}
@Override
public ArrayList<GoblinFrontBannerVo> getListBanner() {
......@@ -806,7 +811,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
if (StringUtil.isNotBlank(spuids)) {
query.addCriteria(Criteria.where("spuId").nin(spuids.split(",")));
}
query.addCriteria(Criteria.where("delFlg").is("0").and("shelvesStatus").is("3").and("spuAppear").is("0").and("marketId").is(null).and("cateFid").nin("22196120924543", "22196122839313"));
query.addCriteria(Criteria.where("delFlg").is("0").and("shelvesStatus").is("3").and("spuAppear").is("0").and("marketId").is(null).and("cateFid").nin("22196120924543", "22196122839313").and("name").not().regex(Pattern.compile(SELECT_GOODS_EXCLUDE_NAME)));
//redis里面获取排序规则 1、上架时间2、销量3、价格高到低4、价格低到高
......@@ -924,7 +929,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
it.remove();
} else {
GoblinGoodsInfoVo goblinGoodsInfoVo = goblinRedisUtils.getGoodsInfoVo(goblinFrontSelectGoods.getSpuId());
if (null == goblinGoodsInfoVo || StringUtil.isNotBlank(goblinGoodsInfoVo.getMarketId())) {
if (null == goblinGoodsInfoVo || StringUtil.isNotBlank(goblinGoodsInfoVo.getMarketId()) || isPassportExclusive(goblinGoodsInfoVo)) {
it.remove();
}
}
......@@ -966,6 +971,9 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
if (isHidden(goblinGoodsInfoVo.getCateFid())) {
continue;
}
if (isPassportExclusive(goblinGoodsInfoVo)) {
continue;
}
goblinGoodsInfoVoArrayList.add(goblinGoodsInfoVo);
}
}
......@@ -1018,9 +1026,15 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
for (int i = 0; i < end; i++) {
if (i >= start) {
GoblinGoodsInfoVo goblinGoodsInfoVo = goblinRedisUtils.getGoodsInfoVo(spuidss[i]);
if (null == goblinGoodsInfoVo) {
continue;
}
if (isHidden(goblinGoodsInfoVo.getCateFid())) {
continue;
}
if (isPassportExclusive(goblinGoodsInfoVo)) {
continue;
}
goblinGoodsInfoVoArrayList.add(goblinGoodsInfoVo);
}
}
......
......@@ -366,6 +366,17 @@ public class KylinPerformancesServiceImpl implements IKylinPerformancesService {
}
}
}
if (ticketListNew.size() > 1) {
ticketListNew.sort((t1, t2) -> {
if (t1 == null || t1.getPrice() == null) {
return (t2 == null || t2.getPrice() == null) ? 0 : 1;
}
if (t2 == null || t2.getPrice() == null) {
return -1;
}
return t1.getPrice().compareTo(t2.getPrice());
});
}
partner.setTicketList(ticketListNew);
if (ticketListNew.size() == 0) {
ticketTimesList.remove(i);
......
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