增加家长测试孩子性格的接口

This commit is contained in:
Wang 2026-01-09 13:44:03 +08:00
parent 7ccea276e8
commit 6b9b01d6e5
19 changed files with 331 additions and 10 deletions

View File

@ -24,6 +24,8 @@ spring:
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
- optional:nacos:shared-database.yaml
- optional:nacos:shared-redis.yaml
- optional:nacos:shared-sa-token.yaml
- optional:nacos:shared-minio.yaml
cloud:
nacos:
discovery:

View File

@ -115,14 +115,23 @@ public interface CommonFileService {
String uploadTextBook(InputStream inputStream, String fileName, String folderName, String contentType);
/**
* 通过输入流上传视频
* 通过输入流上传
*
* @param inputStream 要上传视频的输入流
* @param inputStream 要上传的输入流
* @param folderName 文件夹名称
* @return 视频在OSS上的访问地址
* @return 文件在OSS上的访问地址
*/
String upload(InputStream inputStream,String bucketName, String folderName,String fileName,String contentType);
/**
* 通过输入流上传
*
* @param inputStream 要上传的输入流
* @param folderName 文件夹名称
* @return 文件在OSS上的访问地址
*/
String upload(InputStream inputStream,String bucketName, String folderName,String contentType);
/**
* 从MinIO下载文件
*

View File

@ -304,6 +304,13 @@ public class CommonFileServiceImpl implements CommonFileService {
return MinioConfig.URL + "/" + bucketName + "/" + folderName + "/" + originalFilename;
}
@Override
public String upload(InputStream inputStream, String bucketName, String folderName, String contentType) {
String originalFilename = CommonUtils.generateUniqueFileName();
minioService.upload(bucketName, folderName, originalFilename, inputStream, contentType);
return MinioConfig.URL + "/" + bucketName + "/" + folderName + "/" + originalFilename;
}
/**
* 从MinIO下载文件
*

File diff suppressed because one or more lines are too long

View File

@ -112,4 +112,11 @@ public class MpActivityInfoCollectionEntity extends BaseEntity {
@TableField("strong_subject_ids")
private List<Integer> strongSubjectIds;
/**
* 代理商拨打电话联系家长的次数
*/
@TableField("contact_call_count")
private Integer contactCallCount = 0;
}

View File

@ -52,6 +52,16 @@
<artifactId>lombok-mapstruct-binding</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seer-teacher-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>seer-teacher-service</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -8,6 +8,7 @@ import com.seer.teach.common.annotation.DecryptionAnnotation;
import com.seer.teach.common.annotation.EncryptionAnnotation;
import com.seer.teach.common.annotation.LogPrint;
import com.seer.teach.mp.app.controller.req.AgentActivityParentQueryReq;
import com.seer.teach.mp.app.controller.req.RecordContactCallReq;
import com.seer.teach.mp.app.controller.resp.AgentActivityParentInfoResp;
import com.seer.teach.mp.app.service.IAppAgentActivityParentInfoService;
import io.swagger.v3.oas.annotations.Operation;
@ -46,4 +47,12 @@ public class AppAgentActivityParentInfoController {
Integer userId = StpUtil.getLoginIdAsInt();
return ResultBean.success(agentActivityParentInfoService.getParentsByActivityAndAgent(userId, queryReq));
}
@Operation(summary = "记录代理商拨打电话联系家长的次数")
@PostMapping("/record-contact-call")
@SaCheckLogin
public ResultBean<Boolean> recordContactCall(@RequestBody @Valid RecordContactCallReq req) {
Integer userId = StpUtil.getLoginIdAsInt();
return ResultBean.success(agentActivityParentInfoService.recordContactCall(req.getCollectionId(), req.getIncrementCount(),userId));
}
}

View File

@ -6,12 +6,15 @@ import com.seer.teach.common.ResultBean;
import com.seer.teach.common.annotation.DecryptionAnnotation;
import com.seer.teach.common.annotation.EncryptionAnnotation;
import com.seer.teach.common.annotation.LogPrint;
import com.seer.teach.mp.app.controller.req.AppMpAgentActivityQrCodeQueryReq;
import com.seer.teach.mp.app.controller.resp.AgentActivityParticipantResp;
import com.seer.teach.mp.app.service.IAppAgentActivityParticipantService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@ -26,7 +29,7 @@ import java.util.List;
* @author Lingma
* @since 2025-12-29
*/
@Tag(name = "APP - 代理商参与活动记录")
@Tag(name = "APP - 代理商参与活动")
@RestController
@RequestMapping("/app/agent/activity/participant")
@LogPrint
@ -44,4 +47,12 @@ public class AppAgentActivityParticipantController {
Integer userId = StpUtil.getLoginIdAsInt();
return ResultBean.success(agentActivityParticipantService.getParticipantsByActivityAndAgent(agentId,userId));
}
@Operation(summary = "获取代理参与活动的二维码")
@PostMapping("/qrcode")
@SaCheckPermission("mp:app:agent:activity:qrcode")
public ResultBean<String> getQrCode(@RequestBody AppMpAgentActivityQrCodeQueryReq req) {
Integer userId = StpUtil.getLoginIdAsInt();
return ResultBean.success(agentActivityParticipantService.getQrCode(req,userId));
}
}

View File

@ -34,7 +34,7 @@ import org.springframework.web.bind.annotation.RestController;
@EncryptionAnnotation
@DecryptionAnnotation
@RequiredArgsConstructor
public class AppAgentActivityController {
public class AppMpActivityController {
private final IAppActivityService agentActivityService;

View File

@ -4,10 +4,13 @@ import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import com.seer.teach.common.ResultBean;
import com.seer.teach.mp.app.controller.req.AppMpSignUpActivityReq;
import com.seer.teach.mp.app.controller.req.TestChildCharacterReq;
import com.seer.teach.mp.app.controller.resp.AppMpSignUpActivityResp;
import com.seer.teach.mp.app.controller.resp.TestChildCharacterResp;
import com.seer.teach.mp.app.service.AppParentAgentActivityService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@ -59,4 +62,12 @@ public class AppParentAgentActivityController {
Integer parentId = StpUtil.getLoginIdAsInt();
return ResultBean.success(appParentAgentActivityService.getByActivityIdAndParentId(agentId,activityId,parentId));
}
@PostMapping("/test-child-character")
@SaCheckLogin
@Operation(summary = "测试孩子性格")
public ResultBean<TestChildCharacterResp> testChildCharacter(@RequestBody @Valid TestChildCharacterReq request) {
Integer parentId = StpUtil.getLoginIdAsInt();
return ResultBean.success(appParentAgentActivityService.testChildCharacter(request, parentId));
}
}

View File

@ -0,0 +1,21 @@
package com.seer.teach.mp.app.controller.req;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "App 代理商活动二维码查询请求参数")
public class AppMpAgentActivityQrCodeQueryReq {
@Schema(description = "代理商id",example = "1")
private Integer agentId;
@Schema(description = "活动id",example = "1")
private Integer activityId;
@Schema(description = "公众号appId",example = "wx1234567890")
private String appId;
@Schema(description = "二维码类型 1:临时 2:永久",example = "1")
private Integer qrCodeType;
}

View File

@ -0,0 +1,18 @@
package com.seer.teach.mp.app.controller.req;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
@Schema(description = "记录代理商拨打电话联系家长次数请求对象")
@Data
public class RecordContactCallReq {
@NotNull(message = "活动信息收集记录ID不能为空")
@Schema(description = "活动信息收集记录ID")
private Integer collectionId;
@Schema(description = "增加的联系次数默认为1")
private Integer incrementCount = 1;
}

View File

@ -0,0 +1,35 @@
package com.seer.teach.mp.app.controller.req;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
@Schema(description = "测试孩子性格请求对象")
@Data
public class TestChildCharacterReq {
@NotNull(message = "孩子性别不能为空")
@Schema(description = "孩子性别(M-男,F-女)")
private String childGender;
@NotNull(message = "出生年月不能为空")
@Schema(description = "出生年月")
private LocalDate childBirthDate;
@Schema(description = "星座")
private String constellation;
@NotNull(message = "年级不能为空")
@Schema(description = "年级")
private String grade;
@NotNull(message = "学习情况不能为空")
@Schema(description = "学习情况(优、良、中、差)")
private String learningSituation;
@NotNull(message = "偏科情况不能为空")
@Schema(description = "偏科(数学、英语等)")
private String weakSubject;
}

View File

@ -55,5 +55,8 @@ public class AgentActivityParentInfoResp {
@Schema(description = "劣势学科ID列表")
private List<Integer> weakSubjectIds;
@Schema(description = "代理商拨打电话联系家长的次数")
private Integer contactCallCount;
}

View File

@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.mp.app.controller.req.AppMpSignUpActivityReq;
import com.seer.teach.mp.app.controller.req.TestChildCharacterReq;
import com.seer.teach.mp.app.controller.resp.AppMpSignUpActivityResp;
import com.seer.teach.mp.app.controller.resp.TestChildCharacterResp;
import com.seer.teach.mp.app.convert.AppMpActivityInfoCollectionConvert;
import com.seer.teach.mp.entity.MpActivityEntity;
import com.seer.teach.mp.entity.MpActivityInfoCollectionEntity;
@ -16,24 +18,28 @@ import com.seer.teach.mp.service.IMpActivityService;
import com.seer.teach.mp.service.IMpAgentActivityParticipantService;
import com.seer.teach.mp.service.IMpAgentService;
import com.seer.teach.mp.service.IMpParentAgentActivityRelationService;
import com.seer.teach.teacher.service.AiModelCallService;
import com.seer.teach.teacher.service.platform.LlmResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Objects;
@RequiredArgsConstructor
@Slf4j
@Service
public class AppParentAgentActivityService {
public class AppParentAgentActivityService implements IAppParentAgentActivityService {
private final IMpParentAgentActivityRelationService parentAgentActivityRelationService;
private final IMpActivityInfoCollectionService activityInfoCollectionService;
private final IMpActivityService activityService;
private final IMpAgentService agentService;
private final IMpAgentActivityParticipantService mpAgentActivityParticipantService;
private final AiModelCallService aiModelCallService;
/**
* 报名活动
@ -134,4 +140,92 @@ public class AppParentAgentActivityService {
return AppMpActivityInfoCollectionConvert.INSTANCE.convert2Resp(activityInfoCollection);
}
/**
* 测试孩子性格
*
* @param request 测试请求对象
* @param parentId 家长ID
* @return 性格测试结果
*/
public TestChildCharacterResp testChildCharacter(TestChildCharacterReq request, Integer parentId) {
log.info("开始测试孩子性格家长ID: {},请求参数: {}", parentId, request);
// 构建AI模型的提示词
String prompt = buildCharacterTestPrompt(request);
// 调用AI模型获取性格分析
LlmResponse response = aiModelCallService.callModel("child_character_analysis", prompt, new HashMap<>(), parentId);
TestChildCharacterResp result = new TestChildCharacterResp();
if (response != null && response.getChoices() != null && !response.getChoices().isEmpty()) {
// 获取AI返回的内容
String content = response.getChoices().get(0).getMessage().getContent();
result = parseAiResponse(content);
} else {
// 如果AI调用失败返回默认响应
result.setCharacterAnalysis("暂时无法分析孩子的性格,请稍后重试。");
result.setCharacterTraits("未知");
result.setSuggestions("请尝试重新分析或咨询专业教育专家。");
result.setCharacterType("待定");
}
log.info("性格测试完成,返回结果: {}", result);
return result;
}
/**
* 构建性格测试的提示词
*
* @param request 请求参数
* @return 提示词内容
*/
private String buildCharacterTestPrompt(TestChildCharacterReq request) {
StringBuilder prompt = new StringBuilder();
prompt.append("请根据以下孩子信息分析其性格特点,并给出教育建议:\n");
prompt.append("- 性别: ").append(request.getChildGender()).append("\n");
prompt.append("- 出生日期: ").append(request.getChildBirthDate()).append("\n");
prompt.append("- 星座: ").append(request.getConstellation() != null ? request.getConstellation() : "未知").append("\n");
prompt.append("- 年级: ").append(request.getGrade()).append("\n");
prompt.append("- 学习情况: ").append(request.getLearningSituation()).append("\n");
prompt.append("- 偏科情况: ").append(request.getWeakSubject()).append("\n");
prompt.append("\n请按照以下格式返回分析结果\n");
prompt.append("1. 性格分析:[具体分析内容]\n");
prompt.append("2. 性格特征:[关键特征]\n");
prompt.append("3. 教育建议:[针对性建议]\n");
prompt.append("4. 性格类型:[简要分类]");
return prompt.toString();
}
/**
* 解析AI返回的结果
*
* @param aiResponse AI返回的原始内容
* @return 解析后的响应对象
*/
private TestChildCharacterResp parseAiResponse(String aiResponse) {
TestChildCharacterResp resp = new TestChildCharacterResp();
// 简单解析AI返回的内容
String[] parts = aiResponse.split("\n");
for (String part : parts) {
if (part.startsWith("1. 性格分析:") || part.contains("性格分析")) {
resp.setCharacterAnalysis(part.replaceFirst("^[0-9]+\\.\\s*", "").replace("性格分析:", "").trim());
} else if (part.startsWith("2. 性格特征:") || part.contains("性格特征")) {
resp.setCharacterTraits(part.replaceFirst("^[0-9]+\\.\\s*", "").replace("性格特征:", "").trim());
} else if (part.startsWith("3. 教育建议:") || part.contains("教育建议")) {
resp.setSuggestions(part.replaceFirst("^[0-9]+\\.\\s*", "").replace("教育建议:", "").trim());
} else if (part.startsWith("4. 性格类型:") || part.contains("性格类型")) {
resp.setCharacterType(part.replaceFirst("^[0-9]+\\.\\s*", "").replace("性格类型:", "").trim());
}
}
// 如果解析失败将整个内容作为性格分析
if (resp.getCharacterAnalysis() == null || resp.getCharacterAnalysis().isEmpty()) {
resp.setCharacterAnalysis(aiResponse);
}
return resp;
}
}

View File

@ -33,4 +33,14 @@ public interface IAppAgentActivityParentInfoService {
* @return 分页的家长信息列表
*/
PageListBean<AgentActivityParentInfoResp> getParentsByActivityAndAgent(Integer userId,AgentActivityParentQueryReq queryReq);
/**
* 记录代理商拨打电话联系家长的次数
*
* @param collectionId 活动信息收集记录ID
* @param incrementCount 增加的联系次数
* @param userId 用户ID
* @return 更新后的联系次数
*/
boolean recordContactCall(Integer collectionId, Integer incrementCount,Integer userId);
}

View File

@ -1,5 +1,6 @@
package com.seer.teach.mp.app.service;
import com.seer.teach.mp.app.controller.req.AppMpAgentActivityQrCodeQueryReq;
import com.seer.teach.mp.app.controller.resp.AgentActivityParticipantResp;
import java.util.List;
@ -23,4 +24,12 @@ public interface IAppAgentActivityParticipantService {
*/
List<AgentActivityParticipantResp> getParticipantsByActivityAndAgent(Integer agentId,Integer userId);
/**
* 获取二维码
*
* @param req 请求参数
* @param userId 用户Id
* @return 二维码的url
*/
String getQrCode(AppMpAgentActivityQrCodeQueryReq req, Integer userId);
}

View File

@ -84,6 +84,23 @@ public class AppAgentActivityParentInfoServiceImpl implements IAppAgentActivityP
return PageConverterUtils.convertPageList(pageResult,this::convertToResp);
}
@Override
public boolean recordContactCall(Integer collectionId, Integer incrementCount,Integer userId) {
// 查询指定的活动信息收集记录
MpActivityInfoCollectionEntity entity = activityInfoCollectionService.getById(collectionId);
if (entity == null) {
throw new CommonException(ResultCodeEnum.ACTIVITY_NOT_FOUND);
}
// 更新联系次数
Integer currentCount = entity.getContactCallCount();
if (currentCount == null) {
currentCount = 0;
}
Integer newCount = currentCount + incrementCount;
entity.setContactCallCount(newCount);
return activityInfoCollectionService.updateById(entity);
}
private AgentActivityParentInfoResp convertToResp(MpActivityInfoCollectionEntity entity) {
AgentActivityParentInfoResp resp = new AgentActivityParentInfoResp();
resp.setId(entity.getId());

View File

@ -1,20 +1,30 @@
package com.seer.teach.mp.app.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpDownloader;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.exception.CommonException;
import com.seer.teach.common.service.CommonFileService;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.mp.app.controller.req.AppMpAgentActivityQrCodeQueryReq;
import com.seer.teach.mp.app.controller.req.MpGenerateQrCodeReq;
import com.seer.teach.mp.app.controller.resp.AgentActivityParticipantResp;
import com.seer.teach.mp.app.controller.resp.MpQrCodeResp;
import com.seer.teach.mp.app.service.AppOfficialQrCodeService;
import com.seer.teach.mp.app.service.IAppAgentActivityParticipantService;
import com.seer.teach.mp.app.service.IAppAgentService;
import com.seer.teach.mp.entity.MpActivityEntity;
import com.seer.teach.mp.entity.MpAgentActivityParticipantEntity;
import com.seer.teach.mp.service.IMpActivityService;
import com.seer.teach.mp.service.IMpAgentActivityParticipantService;
import com.seer.teach.user.api.UserInfoServiceApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -36,12 +46,14 @@ public class AppAgentActivityParticipantServiceImpl implements IAppAgentActivity
private final IMpAgentActivityParticipantService agentActivityParticipantService;
private final UserInfoServiceApi userInfoServiceApi;
private final IAppAgentService appAgentService;
private final IMpActivityService mpActivityService;
private final AppOfficialQrCodeService officialQrCodeService;
private final CommonFileService commonFileService;
@Override
public List<AgentActivityParticipantResp> getParticipantsByActivityAndAgent(Integer agentId, Integer userId) {
var userAgentId = appAgentService.getAgentIdByUserId(userId);
@ -68,6 +80,42 @@ public class AppAgentActivityParticipantServiceImpl implements IAppAgentActivity
.collect(Collectors.toList());
}
@Override
public String getQrCode(AppMpAgentActivityQrCodeQueryReq req, Integer userId) {
Integer activityId = req.getActivityId();
Integer agentId = req.getAgentId();
log.info("getQrCode param activityId:{},agentId:{}", activityId, agentId);
MpActivityEntity activity = mpActivityService.getById(activityId);
AssertUtils.notNull(activity, ResultCodeEnum.INVALID_ACTIVITY);
if(activity.getStatus() != 1){
throw new CommonException(ResultCodeEnum.INVALID_ACTIVITY);
}
MpAgentActivityParticipantEntity relation = agentActivityParticipantService.getParticipantsByActivityAndAgent(activityId, agentId);
AssertUtils.notNull(relation, ResultCodeEnum.INVALID_ACTIVITY);
if(StringUtils.isNotBlank(relation.getQrCodeUrl())){
return relation.getQrCodeUrl();
}
MpGenerateQrCodeReq generateQrCodeReq = new MpGenerateQrCodeReq();
generateQrCodeReq.setSceneStr("agentId=" + agentId + "&activityId=" + activityId);
generateQrCodeReq.setAppId(req.getAppId());
generateQrCodeReq.setType(2);
MpQrCodeResp mpQrCodeResp = officialQrCodeService.generateQrCode(generateQrCodeReq);
if(StringUtils.isNotEmpty(mpQrCodeResp.getQrCodeUrl())){
try {
byte[] bytes = HttpDownloader.downloadBytes(mpQrCodeResp.getQrCodeUrl());
InputStream inputStream = new ByteArrayInputStream(bytes);
String url = commonFileService.upload(inputStream,"qrcode", "agent_activity", MediaType.IMAGE_PNG_VALUE);
relation.setQrCodeUrl(url);
}catch (Exception e){
log.error("download qrcode error", e);
relation.setQrCodeUrl(mpQrCodeResp.getQrCodeUrl());
}
}
agentActivityParticipantService.updateById(relation);
return mpQrCodeResp.getQrCodeUrl();
}
private AgentActivityParticipantResp convertToDto(MpAgentActivityParticipantEntity entity, Map<Integer, MpActivityEntity> activityInfoMap) {
AgentActivityParticipantResp resp = new AgentActivityParticipantResp();