package com.talentcard.server.business.admin;
|
|
import java.time.LocalDate;
|
import java.util.List;
|
import java.util.Map;
|
|
import com.talentcard.server.business.PageResult;
|
import com.talentcard.server.common.ApiResponse;
|
import com.talentcard.server.request.TicketPackageCodeImportRequest;
|
import com.talentcard.server.request.TicketPackageCodeGenerateRequest;
|
import com.talentcard.server.request.TicketPackageCodeLockRequest;
|
import com.talentcard.server.request.TicketPackageCodeStatusRequest;
|
import com.talentcard.server.request.TicketPackageSupplementRequest;
|
import com.talentcard.server.request.TicketPackageSaveRequest;
|
import com.talentcard.server.request.TicketPackageStatusRequest;
|
import com.talentcard.server.service.TicketPackageAdminService;
|
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
import jakarta.validation.Valid;
|
import org.springframework.http.MediaType;
|
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.PatchMapping;
|
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PutMapping;
|
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.multipart.MultipartFile;
|
|
@Tag(name = "后台-票包码池", description = "后台维护票包、兑换码码池、库存、发放记录和兑换码状态")
|
@RestController
|
@RequestMapping("/api/admin/ticket-packages")
|
public class TicketPackageAdminController {
|
private final TicketPackageAdminService service;
|
|
public TicketPackageAdminController(TicketPackageAdminService service) {
|
this.service = service;
|
}
|
|
@Operation(summary = "查询票包列表", description = "分页查询票包配置和兑换码库存汇总")
|
@GetMapping
|
public ApiResponse<PageResult<Map<String, Object>>> packages(
|
@Parameter(description = "指定兑换商家ID") @RequestParam(required = false) Long merchantId,
|
@Parameter(description = "状态:1启用,0停用") @RequestParam(required = false) Integer status,
|
@Parameter(description = "票包名称或编号关键词") @RequestParam(required = false) String keyword,
|
@Parameter(description = "适用商家名称关键词") @RequestParam(required = false) String merchantKeyword,
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int page,
|
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int size
|
) {
|
return ApiResponse.success(service.packages(merchantId, status, keyword, merchantKeyword, page, size));
|
}
|
|
@Operation(summary = "查询票包详情", description = "查看票包基础配置和码池库存汇总")
|
@GetMapping("/{packageId}")
|
public ApiResponse<Map<String, Object>> packageDetail(@PathVariable Long packageId) {
|
return ApiResponse.success(service.packageDetail(packageId));
|
}
|
|
@Operation(summary = "票包适用商家选项", description = "票包表单的「适用商家」下拉,只返回启用的观影服务商家")
|
@GetMapping("/merchant-options")
|
public ApiResponse<List<Map<String, Object>>> merchantOptions(
|
@Parameter(description = "商家名称关键词") @RequestParam(required = false) String keyword
|
) {
|
return ApiResponse.success(service.merchantOptions(keyword));
|
}
|
|
@Operation(summary = "查询票包库存", description = "查看指定票包兑换码总量、可发放、锁定、已发放、已使用和作废数量")
|
@GetMapping("/{packageId}/stock")
|
public ApiResponse<Map<String, Object>> stock(@Parameter(description = "票包ID") @PathVariable Long packageId) {
|
return ApiResponse.success(service.stock(packageId));
|
}
|
|
@Operation(summary = "新增票包", description = "后台录入票包,不涉及具体电影票场次")
|
@PostMapping
|
public ApiResponse<Map<String, Object>> createPackage(@Valid @RequestBody TicketPackageSaveRequest request) {
|
return ApiResponse.success(service.createPackage(request));
|
}
|
|
@Operation(summary = "修改票包", description = "修改票包名称、适用卡等级、有效期、商家APP信息等")
|
@PutMapping("/{packageId}")
|
public ApiResponse<Map<String, Object>> updatePackage(
|
@PathVariable Long packageId,
|
@Valid @RequestBody TicketPackageSaveRequest request
|
) {
|
return ApiResponse.success(service.updatePackage(packageId, request));
|
}
|
|
@Operation(summary = "修改票包状态", description = "启用或停用票包")
|
@PatchMapping("/{packageId}/status")
|
public ApiResponse<Map<String, Object>> updatePackageStatus(
|
@PathVariable Long packageId,
|
@Valid @RequestBody TicketPackageStatusRequest request
|
) {
|
return ApiResponse.success(service.updatePackageStatus(packageId, request.status()));
|
}
|
|
@Operation(summary = "导入票包兑换码", description = "批量导入兑换码到指定票包码池,重复兑换码自动跳过")
|
@PostMapping("/{packageId}/codes/import")
|
public ApiResponse<Map<String, Object>> importCodes(
|
@PathVariable Long packageId,
|
@Valid @RequestBody TicketPackageCodeImportRequest request
|
) {
|
return ApiResponse.success(service.importCodes(packageId, request));
|
}
|
|
@Operation(summary = "批量生成票包兑换码", description = "按数量自动生成兑换码并写入指定票包码池,重复兑换码自动跳过")
|
@PostMapping("/{packageId}/codes/generate")
|
public ApiResponse<Map<String, Object>> generateCodes(
|
@PathVariable Long packageId,
|
@Valid @RequestBody TicketPackageCodeGenerateRequest request
|
) {
|
return ApiResponse.success(service.generateCodes(packageId, request));
|
}
|
|
@Operation(summary = "导入票包兑换码Excel", description = "上传Excel文件导入兑换码,读取第一个Sheet第一列,首行可为兑换码标题,重复兑换码自动跳过")
|
@PostMapping(value = "/{packageId}/codes/import-file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
public ApiResponse<Map<String, Object>> importCodeFile(
|
@Parameter(description = "票包ID") @PathVariable Long packageId,
|
@Parameter(description = "兑换码Excel文件") @RequestParam("file") MultipartFile file,
|
@Parameter(description = "导入批次号;不传后端自动生成") @RequestParam(required = false) String batchNo,
|
@Parameter(description = "备注") @RequestParam(required = false) String remark
|
) {
|
return ApiResponse.success(service.importCodeFile(packageId, file, batchNo, remark));
|
}
|
|
@Operation(summary = "查询票包兑换码列表", description = "分页查询码池兑换码,支持状态、批次号和关键词筛选")
|
@GetMapping("/codes")
|
public ApiResponse<PageResult<Map<String, Object>>> codes(
|
@Parameter(description = "票包ID") @RequestParam(required = false) Long packageId,
|
@Parameter(description = "兑换码状态") @RequestParam(required = false) String status,
|
@Parameter(description = "导入批次号") @RequestParam(required = false) String batchNo,
|
@Parameter(description = "兑换码或票包名称关键词") @RequestParam(required = false) String keyword,
|
@Parameter(description = "票包名称或编号关键词") @RequestParam(required = false) String packageKeyword,
|
@Parameter(description = "适用商家名称关键词") @RequestParam(required = false) String merchantKeyword,
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int page,
|
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int size
|
) {
|
return ApiResponse.success(service.codes(packageId, status, batchNo, keyword, packageKeyword, merchantKeyword, page, size));
|
}
|
|
@Operation(summary = "修改兑换码状态", description = "后台作废兑换码,或将已发放兑换码标记为已使用")
|
@PatchMapping("/codes/{codeId}/status")
|
public ApiResponse<Map<String, Object>> updateCodeStatus(
|
@PathVariable Long codeId,
|
@Valid @RequestBody TicketPackageCodeStatusRequest request
|
) {
|
return ApiResponse.success(service.updateCodeStatus(codeId, request));
|
}
|
|
@Operation(summary = "查询票包发放记录", description = "分页查询用户领取兑换码记录")
|
@GetMapping("/claims")
|
public ApiResponse<PageResult<Map<String, Object>>> claims(
|
@Parameter(description = "票包ID") @RequestParam(required = false) Long packageId,
|
@Parameter(description = "持卡人ID") @RequestParam(required = false) Long talentId,
|
@Parameter(description = "发放状态") @RequestParam(required = false) String status,
|
@Parameter(description = "领取编号或兑换码关键词") @RequestParam(required = false) String keyword,
|
@Parameter(description = "票包名称或编号关键词") @RequestParam(required = false) String packageKeyword,
|
@Parameter(description = "持卡人姓名、手机号或卡号关键词") @RequestParam(required = false) String talentKeyword,
|
@Parameter(description = "卡片等级") @RequestParam(required = false) String cardLevel,
|
@Parameter(description = "领取开始日期") @RequestParam(required = false) LocalDate claimedStart,
|
@Parameter(description = "领取结束日期") @RequestParam(required = false) LocalDate claimedEnd,
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int page,
|
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int size
|
) {
|
return ApiResponse.success(service.claims(packageId, talentId, status, keyword, packageKeyword, talentKeyword, cardLevel, claimedStart, claimedEnd, page, size));
|
}
|
|
@Operation(summary = "查询票包核销记录", description = "分页查询已核销的票包领取记录,支持按票包、持卡人、卡片等级、领取时间、核销时间和核销商家筛选")
|
@GetMapping("/verifications")
|
public ApiResponse<PageResult<Map<String, Object>>> verifications(
|
@Parameter(description = "票包ID") @RequestParam(required = false) Long packageId,
|
@Parameter(description = "持卡人姓名、手机号或卡号关键词") @RequestParam(required = false) String talentKeyword,
|
@Parameter(description = "卡片等级") @RequestParam(required = false) String cardLevel,
|
@Parameter(description = "领取开始日期") @RequestParam(required = false) LocalDate claimedStart,
|
@Parameter(description = "领取结束日期") @RequestParam(required = false) LocalDate claimedEnd,
|
@Parameter(description = "核销开始日期") @RequestParam(required = false) LocalDate verifiedStart,
|
@Parameter(description = "核销结束日期") @RequestParam(required = false) LocalDate verifiedEnd,
|
@Parameter(description = "核销商家名称关键词") @RequestParam(required = false) String merchantKeyword,
|
@Parameter(description = "页码") @RequestParam(defaultValue = "1") int page,
|
@Parameter(description = "每页条数") @RequestParam(defaultValue = "10") int size
|
) {
|
return ApiResponse.success(service.verifications(packageId, talentKeyword, cardLevel, claimedStart, claimedEnd, verifiedStart, verifiedEnd, merchantKeyword, page, size));
|
}
|
|
@Operation(summary = "领取校验", description = "按兑换码码值校验兑换码是否可领取:是否存在、所属票包是否启用、是否在有效期内、是否适用于指定商户,以及当前状态(可用/锁定/已发放/已使用/已作废)")
|
@GetMapping("/codes/validate")
|
public ApiResponse<Map<String, Object>> validateCode(
|
@Parameter(description = "兑换码码值", required = true) @RequestParam("code") String code,
|
@Parameter(description = "票包ID;限定校验范围") @RequestParam(required = false) Long packageId,
|
@Parameter(description = "适用商户ID;不匹配时返回不可领取") @RequestParam(required = false) Long merchantId
|
) {
|
return ApiResponse.success(service.validateCode(code, packageId, merchantId));
|
}
|
|
@Operation(summary = "兑换码详情", description = "查看兑换码的码值、适用商户、有效期、库存状态、锁定信息和发放/领取状态(领取人、使用状态、使用时间)")
|
@GetMapping("/codes/{codeId}")
|
public ApiResponse<Map<String, Object>> codeDetail(
|
@Parameter(description = "兑换码ID") @PathVariable Long codeId
|
) {
|
return ApiResponse.success(service.codeDetail(codeId));
|
}
|
|
@Operation(summary = "发放锁定", description = "后台手动锁定一个可用兑换码并绑定领取人,用于线下发放前的占位预留;锁定后可补录发放、解锁或作废")
|
@PostMapping("/codes/{codeId}/lock")
|
public ApiResponse<Map<String, Object>> lockForIssue(
|
@Parameter(description = "兑换码ID") @PathVariable Long codeId,
|
@Valid @RequestBody TicketPackageCodeLockRequest request
|
) {
|
return ApiResponse.success(service.lockForIssue(codeId, request));
|
}
|
|
@Operation(summary = "解锁兑换码", description = "将锁定状态的兑换码解锁回可用状态,并清除锁定领取人信息")
|
@PatchMapping("/codes/{codeId}/release")
|
public ApiResponse<Map<String, Object>> releaseLock(
|
@Parameter(description = "兑换码ID") @PathVariable Long codeId
|
) {
|
return ApiResponse.success(service.releaseLock(codeId));
|
}
|
|
@Operation(summary = "作废兑换码", description = "作废可用、锁定或已发放状态的兑换码;已发放的会同步作废其发放记录,已使用或已作废的兑换码不可作废")
|
@PatchMapping("/codes/{codeId}/void")
|
public ApiResponse<Map<String, Object>> voidCode(
|
@Parameter(description = "兑换码ID") @PathVariable Long codeId,
|
@Parameter(description = "作废备注") @RequestParam(required = false) String remark
|
) {
|
return ApiResponse.success(service.voidCode(codeId, remark));
|
}
|
|
@Operation(summary = "补录发放记录", description = "线下已发放但系统未记录的兑换码,后台按兑换码ID或码值补录一条发放记录并将兑换码标记为已发放;重复补录同一兑换码会返回已有记录")
|
@PostMapping("/claims/supplement")
|
public ApiResponse<Map<String, Object>> supplementClaim(
|
@Valid @RequestBody TicketPackageSupplementRequest request
|
) {
|
return ApiResponse.success(service.supplementClaim(request));
|
}
|
}
|