青核后端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

148 lines
7.0 KiB

package com.campus.controller;
import com.campus.common.Result;
import com.campus.dto.request.XuexinSubmitRequest;
import com.campus.entity.User;
import com.campus.entity.UserPoints;
import com.campus.entity.XuexinReport;
import com.campus.service.AuthorizationService;
import com.campus.service.UserPointsService;
import com.campus.service.UserService;
import com.campus.service.ProfileService;
import com.campus.service.XuexinReportService;
import com.campus.util.OssUtil;
import com.campus.util.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/profile")
@RequiredArgsConstructor
public class ProfileController {
private final UserService userService;
private final ProfileService profileService;
private final UserPointsService userPointsService;
private final OssUtil ossUtil;
private final AuthorizationService authorizationService;
private final XuexinReportService xuexinReportService;
@GetMapping
public Result<User> getMyProfile() {
String userId = UserContext.getCurrentUserId();
return Result.success(userService.getUserById(userId));
}
@GetMapping("/other")
public Result<User> getProfile(@RequestParam String userId) {
return Result.success(userService.getPublicUserById(userId));
}
@PutMapping
public Result<User> updateProfile(@RequestBody User updateData) {
String userId = UserContext.getCurrentUserId();
return Result.success(profileService.updateProfile(UserContext.getCurrentUserId(), userId, updateData));
}
@PutMapping("/other")
public Result<User> updateProfile(
@RequestParam String userId,
@RequestBody User updateData) {
authorizationService.requireAdmin();
return Result.success(profileService.updateProfile(UserContext.getCurrentUserId(), userId, updateData));
}
/**
* 学信网认证 — 提交接口。
*
* <p>前置条件:Step1 调 {@code POST /api/ocr/chsi-report} 已拿到 {@code reportId},
* 且其结果里 {@code dedup.status=AVAILABLE}。</p>
*
* <p>本接口<b>不再信任请求体里的教育字段</b>:姓名/学校/学历等一律以
* {@code xuexin_report} 表中的 OCR 记录为准,请求体只需 {@code reportId}
* 且客户端不能覆盖 PDF 中识别出的认证字段。
* 这样即使前端伪造 {@code validChsiReport},也拿不到认证。</p>
*
* <p>完整校验与写入逻辑见 {@link XuexinReportService#submit}:</p>
* <ul>
* <li>材料不存在 / 不属于当前用户 → 400「未找到认证材料,请重新上传报告」</li>
* <li>材料已过期或被替换 → 400「该认证材料已失效,请重新上传报告」</li>
* <li>已认证且换更低学历 → 400「…不支持更换为更低学历…」</li>
* <li>已认证未满冷静期 → 400「认证通过后 3 天内不可更换认证材料,请在 … 后重试」</li>
* <li>重复提交同一份材料 → 幂等成功,原样返回当前用户</li>
* </ul>
*/
@PostMapping("/xuexin/submit")
public Result<User> submitXuexin(@RequestBody XuexinSubmitRequest req) {
String userId = UserContext.getCurrentUserId();
return Result.success(xuexinReportService.submit(userId, req));
}
/** 上传并立即保存当前用户头像,不依赖编辑资料页的其他字段保存。 */
@PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result<User> uploadAvatar(@RequestParam("file") MultipartFile file) {
String userId = UserContext.getCurrentUserId();
User user = userService.getUserById(userId);
String avatarUrl = ossUtil.uploadAvatar(file);
user.setAvatar(avatarUrl);
userService.saveUser(user);
return Result.success(user);
}
/**
* 查询当前用户学信网认证状态和已确认的学籍信息。
*
* <p>新增 {@code certifiedAt}(认证时间)、{@code reportId}(当前生效材料),
* 以及新版报告上的分院、系所、日期、民族、学历类别、学习形式和在线验证码。</p>
*/
@GetMapping("/xuexin/status")
public Result<Map<String, Object>> xuexinStatus() {
String userId = UserContext.getCurrentUserId();
User user = userService.getUserById(userId);
XuexinReport activeReport = xuexinReportService.findActiveReport(userId);
Map<String, Object> result = new LinkedHashMap<>();
result.put("certified", Boolean.TRUE.equals(user.getXuexinCertified()));
result.put("number", user.getNumber());
result.put("school", user.getUniversityName());
result.put("department", user.getCollegeName());
result.put("major", user.getMajorName());
result.put("level", user.getEducation());
result.put("grade", user.getGrade());
result.put("enrollmentStatus", user.getXuexinEnrollmentStatus());
result.put("certifiedAt", user.getXuexinCertifiedAt());
result.put("reportId", user.getXuexinReportId());
result.put("reportName", activeReport == null ? null : activeReport.getReportName());
result.put("reportUpdatedDate", activeReport == null ? null : activeReport.getReportUpdatedDate());
result.put("name", activeReport == null ? null : activeReport.getName());
result.put("gender", activeReport == null ? null : activeReport.getGender());
result.put("birthDate", activeReport == null ? null : activeReport.getBirthDate());
result.put("ethnicity", activeReport == null ? null : activeReport.getEthnicity());
result.put("studyDuration", activeReport == null ? null : activeReport.getStudyDuration());
result.put("educationCategory", activeReport == null ? null : activeReport.getEducationCategory());
result.put("studyForm", activeReport == null ? null : activeReport.getStudyForm());
result.put("branchCampus", activeReport == null ? null : activeReport.getBranchCampus());
result.put("enrollmentDate", activeReport == null ? null : activeReport.getEnrollmentDate());
result.put("departureDate", activeReport == null ? null : activeReport.getDepartureDate());
result.put("onlineVerificationCode", activeReport == null ? null : activeReport.getOnlineVerificationCode());
return Result.success(result);
}
@GetMapping("/points")
public Result<UserPoints> getMyPoints() {
String userId = UserContext.getCurrentUserId();
return Result.success(userPointsService.getUserPoints(userId));
}
@GetMapping("/points-other")
public Result<UserPoints> getUserPoints(@RequestParam String userId) {
authorizationService.requireAdmin();
return Result.success(userPointsService.getUserPoints(userId));
}
}