package com.xuekairui.user.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.xuekairui.common.Result; import com.xuekairui.user.dto.BusinessLicenseReviewResponse; import com.xuekairui.user.enums.LicenseStatus; import com.xuekairui.user.service.BusinessLicenseService; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; 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.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 java.util.List; /** * 入驻信息审核管理接口(运营端) */ @Slf4j @RestController @RequestMapping("/api/admin/license") @RequiredArgsConstructor public class AdminLicenseController { private final BusinessLicenseService businessLicenseService; /** * 审核列表查询 * @param reviewStatus 审核状态筛选(可选):PENDING/APPROVED/REJECTED,不传则查全部 * @param keyword 手机号关键词(可选):匹配用户手机号或入驻联系电话 */ @GetMapping("/review") public Result> listPendingReviews( @RequestParam(required = false) String reviewStatus, @RequestParam(required = false) String keyword, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "20") int size) { return Result.success(businessLicenseService.listByReviewStatus(reviewStatus, keyword, page, size)); } /** * 管理员新增入驻信息(代客户填写,创建后状态为待审核,需再操作一次审核确认) *

* 必须选择一个未提交过入驻信息的客户,必填基础信息, * 必须上传营业执照、药品经营许可、(二类/三类医疗器械二选一)。 * POST /api/admin/license */ @PostMapping public Result createByAdmin( @Valid @RequestBody AdminCreateRequest request, HttpServletRequest httpRequest) { Long reviewerId = (Long) httpRequest.getAttribute("userId"); businessLicenseService.createByAdmin(request.toSaveRequest(), reviewerId); return Result.success(); } /** * 管理员编辑入驻信息(可修改客户信息、替换资质图片,保存后即时生效) *

* 可编辑全部入驻数据(含已审核),必填项约束同样生效。 * PUT /api/admin/license/{licenseId} */ @PutMapping("/{licenseId}") public Result editByAdmin( @PathVariable Long licenseId, @Valid @RequestBody AdminEditRequest request, HttpServletRequest httpRequest) { Long reviewerId = (Long) httpRequest.getAttribute("userId"); businessLicenseService.editByAdmin(licenseId, request.toSaveRequest(), reviewerId); return Result.success(); } @PostMapping("/review") public Result reviewLicense( @Valid @RequestBody ReviewRequest request, HttpServletRequest httpRequest) { Long reviewerId = (Long) httpRequest.getAttribute("userId"); if (LicenseStatus.APPROVED.name().equals(request.getAction())) { businessLicenseService.approve(request.getLicenseId(), reviewerId); } else if (LicenseStatus.REJECTED.name().equals(request.getAction())) { businessLicenseService.reject(request.getLicenseId(), reviewerId, request.getRejectReason()); } else { return Result.error(400, "审核动作无效,请使用 APPROVED 或 REJECTED"); } return Result.success(); } /** * 运营端手动同步入驻信息到第三方(智价云药店版) *

* 运营在后台选择某条入驻记录,点击"同步到第三方"按钮时调用。 * 同步执行(非异步),便于运营立即看到同步结果。 * POST /api/admin/license/{licenseId}/sync-third */ @PostMapping("/{licenseId}/sync-third") public Result syncToThirdParty( @PathVariable Long licenseId, HttpServletRequest httpRequest) { Long reviewerId = (Long) httpRequest.getAttribute("userId"); String result = businessLicenseService.manualSyncToThirdParty(licenseId, reviewerId); return Result.success(result); } /** * 查询入驻信息同步差异列表(运营端) *

* 当我方与第三方都已审核通过但字段不一致时,差异记录保存到 t_license_sync_diff。 * 运营在此查看待处理差异,决定采纳第三方值、保留本地值或手动调整。 * GET /api/admin/license/sync-diff?status=0 */ @GetMapping("/sync-diff") public Result> listSyncDiff( @RequestParam(required = false) Integer status) { return Result.success(businessLicenseService.listSyncDiff(status)); } /** * 处理入驻信息同步差异(运营端) *

* 运营对每条差异记录决定处理方式: *

* * POST /api/admin/license/sync-diff/{diffId}/handle */ @PostMapping("/sync-diff/{diffId}/handle") public Result handleSyncDiff( @PathVariable Long diffId, @Valid @RequestBody HandleDiffRequest request, HttpServletRequest httpRequest) { Long reviewerId = (Long) httpRequest.getAttribute("userId"); businessLicenseService.handleSyncDiff(diffId, request.getAction(), request.getRemark(), reviewerId); return Result.success(); } @Data public static class ReviewRequest { @NotNull(message = "入驻信息ID不能为空") private Long licenseId; @NotBlank(message = "审核动作不能为空") private String action; private String rejectReason; } /** * 处理入驻信息同步差异请求 */ @Data public static class HandleDiffRequest { /** 处理动作:ADOPT_THIRD-采纳第三方 / KEEP_LOCAL-保留本地 / MANUAL-手动调整 */ @NotBlank(message = "处理动作不能为空") private String action; /** 处理备注(可选) */ private String remark; } /** * 管理员新增入驻信息请求 */ @Data public static class AdminCreateRequest { /** 客户用户ID(必须选择未提交过入驻信息的客户) */ @NotNull(message = "必须选择一个客户") private Long userId; /** 店铺名称 */ @NotBlank(message = "店铺名称不能为空") private String storeName; /** 终端类型:SINGLE-单店, CHAIN-连锁, CLINIC-诊所, COMMUNITY_HEALTH-社康 */ @NotBlank(message = "终端类型不能为空") private String terminalType; /** 省 */ @NotBlank(message = "所在省不能为空") private String province; /** 市 */ @NotBlank(message = "所在市不能为空") private String city; /** 区 */ @NotBlank(message = "所在区不能为空") private String district; /** 店铺详细地址 */ @NotBlank(message = "店铺地址不能为空") private String storeAddress; /** 联系人姓名 */ @NotBlank(message = "联系人姓名不能为空") private String contactPerson; /** 联系电话 */ @NotBlank(message = "联系电话不能为空") private String contactPhone; /** 营业执照图片URL */ @NotBlank(message = "营业执照图片不能为空") private String businessLicenseUrl; /** 药品经营许可证图片URL */ @NotBlank(message = "药品经营许可证不能为空") private String drugLicenseUrl; /** 统一社会信用代码 */ private String creditCode; /** 二类医疗器械备案图片URL */ private String medicalDeviceClass2Url; /** 三类医疗器械备案图片URL */ private String medicalDeviceClass3Url; public BusinessLicenseService.BusinessLicenseSaveRequest toSaveRequest() { return BusinessLicenseService.BusinessLicenseSaveRequest.builder() .userId(userId) .storeName(storeName) .terminalType(terminalType) .province(province) .city(city) .district(district) .storeAddress(storeAddress) .contactPerson(contactPerson) .contactPhone(contactPhone) .businessLicenseUrl(businessLicenseUrl) .drugLicenseUrl(drugLicenseUrl) .creditCode(creditCode) .medicalDeviceClass2Url(medicalDeviceClass2Url) .medicalDeviceClass3Url(medicalDeviceClass3Url) .build(); } } /** * 管理员编辑入驻信息请求(不含 userId,编辑时客户绑定关系不可更改) */ @Data public static class AdminEditRequest { /** 店铺名称 */ @NotBlank(message = "店铺名称不能为空") private String storeName; /** 终端类型:SINGLE-单店, CHAIN-连锁, CLINIC-诊所, COMMUNITY_HEALTH-社康 */ @NotBlank(message = "终端类型不能为空") private String terminalType; /** 省 */ @NotBlank(message = "所在省不能为空") private String province; /** 市 */ @NotBlank(message = "所在市不能为空") private String city; /** 区 */ @NotBlank(message = "所在区不能为空") private String district; /** 店铺详细地址 */ @NotBlank(message = "店铺地址不能为空") private String storeAddress; /** 联系人姓名 */ @NotBlank(message = "联系人姓名不能为空") private String contactPerson; /** 联系电话 */ @NotBlank(message = "联系电话不能为空") private String contactPhone; /** 营业执照图片URL */ @NotBlank(message = "营业执照图片不能为空") private String businessLicenseUrl; /** 药品经营许可证图片URL */ @NotBlank(message = "药品经营许可证不能为空") private String drugLicenseUrl; /** 统一社会信用代码 */ private String creditCode; /** 二类医疗器械备案图片URL */ private String medicalDeviceClass2Url; /** 三类医疗器械备案图片URL */ private String medicalDeviceClass3Url; public BusinessLicenseService.BusinessLicenseSaveRequest toSaveRequest() { return BusinessLicenseService.BusinessLicenseSaveRequest.builder() .storeName(storeName) .terminalType(terminalType) .province(province) .city(city) .district(district) .storeAddress(storeAddress) .contactPerson(contactPerson) .contactPhone(contactPhone) .businessLicenseUrl(businessLicenseUrl) .drugLicenseUrl(drugLicenseUrl) .creditCode(creditCode) .medicalDeviceClass2Url(medicalDeviceClass2Url) .medicalDeviceClass3Url(medicalDeviceClass3Url) .build(); } } }