mirror of
https://gitee.com/yudaocode/yudao-boot-mini.git
synced 2026-10-06 14:13:14 +08:00
(〃'▽'〃)_v2026_07_发布:管理后台移动端完成全模块适配,增强工作流与 CRM 数据统计
This commit is contained in:
-295
@@ -1,295 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.excel.core.convert;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.idev.excel.metadata.data.ReadCellData;
|
||||
import cn.idev.excel.metadata.data.WriteCellData;
|
||||
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi;
|
||||
import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO;
|
||||
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link MultiDictConvert} 的单元测试
|
||||
*
|
||||
* @author NaCl
|
||||
*/
|
||||
public class MultiDictConvertTest extends BaseMockitoUnitTest {
|
||||
|
||||
private static final String DICT_TYPE = "test_dict";
|
||||
|
||||
@Mock
|
||||
private DictDataCommonApi dictDataApi;
|
||||
|
||||
private MultiDictConvert convert;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
convert = new MultiDictConvert();
|
||||
DictFrameworkUtils.init(dictDataApi);
|
||||
DictFrameworkUtils.clearCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_string() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData("1,2", mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("男、女", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_single() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData("1", mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("男", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_order() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData("2,1", mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("女、男", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_null() {
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData(null, null, null);
|
||||
// 断言
|
||||
assertEquals("", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_collection() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData(ListUtil.of(1, 2),
|
||||
mockContentProperty("listValues"), null);
|
||||
// 断言
|
||||
assertEquals("男、女", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_array() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData(new int[]{1, 2},
|
||||
mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("男、女", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToExcelData_unknownValue() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
WriteCellData<String> result = convert.convertToExcelData("1,3", mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("", result.getStringValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_string() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男、女"),
|
||||
mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("1,2", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_single() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男"),
|
||||
mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("1", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_blank() throws NoSuchFieldException {
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData(""),
|
||||
mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertEquals("", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_blankList() throws NoSuchFieldException {
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData(""),
|
||||
mockContentProperty("listValues"), null);
|
||||
// 断言
|
||||
assertTrue(result instanceof List);
|
||||
assertTrue(((List<?>) result).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_blankSet() throws NoSuchFieldException {
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData(""),
|
||||
mockContentProperty("setValues"), null);
|
||||
// 断言
|
||||
assertTrue(result instanceof LinkedHashSet);
|
||||
assertTrue(((Set<?>) result).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_blankArray() throws NoSuchFieldException {
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData(""),
|
||||
mockContentProperty("arrayValues"), null);
|
||||
// 断言
|
||||
assertArrayEquals(new Integer[0], (Integer[]) result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testConvertToJavaData_list() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男、女"),
|
||||
mockContentProperty("listValues"), null);
|
||||
// 断言
|
||||
assertEquals(ListUtil.of(1, 2), (List<Integer>) result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testConvertToJavaData_set() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男,女"),
|
||||
mockContentProperty("setValues"), null);
|
||||
// 断言
|
||||
assertEquals(new LinkedHashSet<>(Arrays.asList(1L, 2L)), (Set<Long>) result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("SimplifiableAssertion")
|
||||
public void testConvertToJavaData_hashSet() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男,女"),
|
||||
mockContentProperty("hashSetValues"), null);
|
||||
// 断言
|
||||
assertTrue(result instanceof HashSet);
|
||||
assertEquals(new HashSet<>(Arrays.asList(1L, 2L)), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_array() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男,女"),
|
||||
mockContentProperty("arrayValues"), null);
|
||||
// 断言
|
||||
assertArrayEquals(new Integer[]{1, 2}, (Integer[]) result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToJavaData_unknownLabel() throws NoSuchFieldException {
|
||||
// mock 数据
|
||||
mockDictDatas();
|
||||
|
||||
// 调用
|
||||
Object result = convert.convertToJavaData(mockReadCellData("男、未知"),
|
||||
mockContentProperty("stringValues"), null);
|
||||
// 断言
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private void mockDictDatas() {
|
||||
List<DictDataRespDTO> dictDatas = ListUtil.of(
|
||||
randomPojo(DictDataRespDTO.class, o -> o.setDictType(DICT_TYPE).setValue("1").setLabel("男")),
|
||||
randomPojo(DictDataRespDTO.class, o -> o.setDictType(DICT_TYPE).setValue("2").setLabel("女"))
|
||||
);
|
||||
when(dictDataApi.getDictDataList(eq(DICT_TYPE))).thenReturn(dictDatas);
|
||||
}
|
||||
|
||||
private static ReadCellData<?> mockReadCellData(String value) {
|
||||
ReadCellData<?> readCellData = mock(ReadCellData.class);
|
||||
when(readCellData.getStringValue()).thenReturn(value);
|
||||
return readCellData;
|
||||
}
|
||||
|
||||
private static ExcelContentProperty mockContentProperty(String fieldName) throws NoSuchFieldException {
|
||||
Field field = TestExcelVO.class.getDeclaredField(fieldName);
|
||||
ExcelContentProperty contentProperty = mock(ExcelContentProperty.class);
|
||||
when(contentProperty.getField()).thenReturn(field);
|
||||
return contentProperty;
|
||||
}
|
||||
|
||||
static class TestExcelVO {
|
||||
|
||||
@DictFormat(DICT_TYPE)
|
||||
private String stringValues;
|
||||
|
||||
@DictFormat(DICT_TYPE)
|
||||
private List<Integer> listValues;
|
||||
|
||||
@DictFormat(DICT_TYPE)
|
||||
private Set<Long> setValues;
|
||||
|
||||
@DictFormat(DICT_TYPE)
|
||||
private HashSet<Long> hashSetValues;
|
||||
|
||||
@DictFormat(DICT_TYPE)
|
||||
private Integer[] arrayValues;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.comment;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentRespVO;
|
||||
import cn.iocoder.yudao.module.bpm.service.comment.BpmCommentService;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.engine.task.Comment;
|
||||
import org.flowable.task.api.history.HistoricTaskInstance;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - 流程评论")
|
||||
@RestController
|
||||
@RequestMapping("/bpm/comment")
|
||||
@Validated
|
||||
public class BpmCommentController {
|
||||
|
||||
@Resource
|
||||
private BpmCommentService commentService;
|
||||
@Resource
|
||||
private BpmTaskService taskService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@GetMapping("/list-by-process-instance-id")
|
||||
@Operation(summary = "获得指定流程实例的评论列表")
|
||||
@Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
|
||||
public CommonResult<List<BpmCommentRespVO>> getCommentListByProcessInstanceId(
|
||||
@RequestParam("processInstanceId") String processInstanceId) {
|
||||
// 获得评论列表
|
||||
List<Comment> commentList = commentService.getCommentListByProcessInstanceId(processInstanceId);
|
||||
if (CollUtil.isEmpty(commentList)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 拼接 VO
|
||||
Map<String, HistoricTaskInstance> taskMap = taskService.getHistoricTaskMap(
|
||||
convertSet(commentList, Comment::getTaskId));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertSet(commentList, comment -> NumberUtils.parseLong(comment.getUserId())));
|
||||
return success(convertList(commentList, comment -> BeanUtils.toBean(comment, BpmCommentRespVO.class, commentVO -> {
|
||||
commentVO.setMessage(comment.getFullMessage())
|
||||
.setCreateTime(DateUtils.of(comment.getTime()))
|
||||
.setTask(BeanUtils.toBean(taskMap.get(comment.getTaskId()), BpmCommentRespVO.Task.class))
|
||||
.setUser(BeanUtils.toBean(userMap.get(NumberUtils.parseLong(comment.getUserId())), UserSimpleBaseVO.class));
|
||||
})));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建流程评论")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> createComment(@Valid @RequestBody BpmCommentCreateReqVO reqVO) {
|
||||
commentService.createComment(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.comment.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 流程评论创建 Request VO")
|
||||
@Data
|
||||
public class BpmCommentCreateReqVO {
|
||||
|
||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
@NotEmpty(message = "任务编号不能为空")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "请关注报销发票附件")
|
||||
@NotBlank(message = "评论内容不能为空")
|
||||
@Size(max = 500, message = "评论内容不能超过 500 个字符")
|
||||
private String message;
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.controller.admin.comment.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 流程评论 Response VO")
|
||||
@Data
|
||||
public class BpmCommentRespVO {
|
||||
|
||||
@Schema(description = "评论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "任务编号", example = "2048")
|
||||
private String taskId;
|
||||
|
||||
@Schema(description = "任务")
|
||||
private Task task;
|
||||
|
||||
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
|
||||
private String processInstanceId;
|
||||
|
||||
@Schema(description = "评论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "请关注报销发票附件")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "创建人")
|
||||
private UserSimpleBaseVO user;
|
||||
|
||||
@Schema(description = "任务")
|
||||
@Data
|
||||
public static class Task {
|
||||
|
||||
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
|
||||
private String taskDefinitionKey;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.comment;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.engine.task.Comment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程评论 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface BpmCommentService {
|
||||
|
||||
/**
|
||||
* 获得指定流程实例的评论列表
|
||||
*
|
||||
* @param processInstanceId 流程实例的编号
|
||||
* @return 评论列表
|
||||
*/
|
||||
List<Comment> getCommentListByProcessInstanceId(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 创建流程评论
|
||||
*
|
||||
* @param reqVO 评论请求
|
||||
*/
|
||||
void createComment(@Valid BpmCommentCreateReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 创建流程评论
|
||||
*
|
||||
* @param taskId 任务编号
|
||||
* @param processInstanceId 流程实例编号
|
||||
* @param type 评论类型
|
||||
* @param params 评论模板参数
|
||||
*/
|
||||
void createComment(String taskId, String processInstanceId, BpmCommentTypeEnum type, Object... params);
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.comment;
|
||||
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.comment.vo.BpmCommentCreateReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.task.Comment;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 流程评论 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class BpmCommentServiceImpl implements BpmCommentService {
|
||||
|
||||
@Resource
|
||||
private TaskService taskService;
|
||||
@Resource
|
||||
@Lazy // 延迟加载,避免循环依赖
|
||||
private BpmTaskService bpmTaskService;
|
||||
|
||||
@Override
|
||||
public List<Comment> getCommentListByProcessInstanceId(String processInstanceId) {
|
||||
return taskService.getProcessInstanceComments(processInstanceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createComment(@Valid BpmCommentCreateReqVO reqVO) {
|
||||
Task task = bpmTaskService.validateTaskExists(reqVO.getTaskId());
|
||||
createComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.COMMENT, reqVO.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createComment(String taskId, String processInstanceId, BpmCommentTypeEnum type, Object... params) {
|
||||
taskService.addComment(taskId, processInstanceId, type.getType(), type.formatComment(params));
|
||||
}
|
||||
|
||||
}
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
package cn.iocoder.yudao.module.bpm.service.definition;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum;
|
||||
import org.flowable.engine.RepositoryService;
|
||||
import org.flowable.engine.repository.Model;
|
||||
import org.flowable.engine.repository.ModelQuery;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_NOT_EXISTS;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.AdditionalMatchers.aryEq;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link BpmModelServiceImpl} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class BpmModelServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
private static final Long TENANT_ID = 1L;
|
||||
private static final String MODEL_ID = "model-id";
|
||||
|
||||
@InjectMocks
|
||||
private BpmModelServiceImpl modelService;
|
||||
|
||||
@Mock
|
||||
private RepositoryService repositoryService;
|
||||
@Mock
|
||||
private ModelQuery modelQuery;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
TenantContextHolder.setTenantId(TENANT_ID);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
TenantContextHolder.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportModel_bpmn() {
|
||||
// 准备参数
|
||||
Model model = mockModel(BpmModelTypeEnum.BPMN.getType());
|
||||
String bpmnXml = "<definitions />";
|
||||
// mock 方法(repositoryService)
|
||||
mockGetModel(model);
|
||||
when(repositoryService.getModelEditorSource(eq(MODEL_ID))).thenReturn(StrUtil.utf8Bytes(bpmnXml));
|
||||
|
||||
// 调用
|
||||
BpmModelSaveReqVO result = modelService.exportModel(MODEL_ID);
|
||||
|
||||
// 断言
|
||||
assertEquals(model.getKey(), result.getKey());
|
||||
assertEquals(model.getName(), result.getName());
|
||||
assertEquals(model.getCategory(), result.getCategory());
|
||||
assertEquals(BpmModelTypeEnum.BPMN.getType(), result.getType());
|
||||
assertEquals(bpmnXml, result.getBpmnXml());
|
||||
assertNull(result.getSimpleModel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportModel_simple() {
|
||||
// 准备参数
|
||||
Model model = mockModel(BpmModelTypeEnum.SIMPLE.getType());
|
||||
BpmSimpleModelNodeVO simpleModel = new BpmSimpleModelNodeVO();
|
||||
simpleModel.setId("start");
|
||||
simpleModel.setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType());
|
||||
// mock 方法(repositoryService)
|
||||
mockGetModel(model);
|
||||
when(repositoryService.getModelEditorSourceExtra(eq(MODEL_ID)))
|
||||
.thenReturn(JsonUtils.toJsonByte(simpleModel));
|
||||
|
||||
// 调用
|
||||
BpmModelSaveReqVO result = modelService.exportModel(MODEL_ID);
|
||||
|
||||
// 断言
|
||||
assertEquals(BpmModelTypeEnum.SIMPLE.getType(), result.getType());
|
||||
assertNotNull(result.getSimpleModel());
|
||||
assertEquals(simpleModel.getId(), result.getSimpleModel().getId());
|
||||
assertEquals(simpleModel.getType(), result.getSimpleModel().getType());
|
||||
assertNull(result.getBpmnXml());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportModel_notExists() {
|
||||
// mock 方法(repositoryService)
|
||||
when(repositoryService.createModelQuery()).thenReturn(modelQuery);
|
||||
when(modelQuery.modelId(eq(MODEL_ID))).thenReturn(modelQuery);
|
||||
when(modelQuery.modelTenantId(eq(TENANT_ID.toString()))).thenReturn(modelQuery);
|
||||
when(modelQuery.singleResult()).thenReturn(null);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> modelService.exportModel(MODEL_ID), MODEL_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportModel_bpmn() {
|
||||
// 准备参数
|
||||
BpmModelSaveReqVO reqVO = new BpmModelSaveReqVO();
|
||||
reqVO.setKey("test_process");
|
||||
reqVO.setName("测试流程");
|
||||
reqVO.setCategory("OA");
|
||||
reqVO.setType(BpmModelTypeEnum.BPMN.getType());
|
||||
reqVO.setBpmnXml("<definitions />");
|
||||
reqVO.setStartUserIds(Arrays.asList(10L, 20L));
|
||||
reqVO.setStartDeptIds(Collections.singletonList(30L));
|
||||
reqVO.setManagerUserIds(Collections.singletonList(40L));
|
||||
Model model = mock(Model.class);
|
||||
when(model.getId()).thenReturn(MODEL_ID);
|
||||
// mock 方法(repositoryService)
|
||||
when(repositoryService.createModelQuery()).thenReturn(modelQuery);
|
||||
when(modelQuery.modelTenantId(eq(TENANT_ID.toString()))).thenReturn(modelQuery);
|
||||
when(modelQuery.modelKey(eq(reqVO.getKey()))).thenReturn(modelQuery);
|
||||
when(modelQuery.singleResult()).thenReturn(null);
|
||||
when(repositoryService.newModel()).thenReturn(model);
|
||||
|
||||
// 调用
|
||||
String result = modelService.importModel(reqVO);
|
||||
|
||||
// 断言
|
||||
assertEquals(MODEL_ID, result);
|
||||
verify(model).setTenantId(TENANT_ID.toString());
|
||||
verify(repositoryService).saveModel(same(model));
|
||||
verify(repositoryService).addModelEditorSource(eq(MODEL_ID),
|
||||
aryEq(StrUtil.utf8Bytes(reqVO.getBpmnXml())));
|
||||
ArgumentCaptor<String> metaInfoCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(model).setMetaInfo(metaInfoCaptor.capture());
|
||||
BpmModelMetaInfoVO metaInfo = JsonUtils.parseObject(metaInfoCaptor.getValue(), BpmModelMetaInfoVO.class);
|
||||
assertNotNull(metaInfo);
|
||||
assertEquals(Arrays.asList(10L, 20L), metaInfo.getStartUserIds());
|
||||
assertEquals(Collections.singletonList(30L), metaInfo.getStartDeptIds());
|
||||
assertEquals(Collections.singletonList(40L), metaInfo.getManagerUserIds());
|
||||
}
|
||||
|
||||
private Model mockModel(Integer type) {
|
||||
Model model = mock(Model.class);
|
||||
when(model.getKey()).thenReturn("test_process");
|
||||
when(model.getName()).thenReturn("测试流程");
|
||||
when(model.getCategory()).thenReturn("OA");
|
||||
when(model.getCreateTime()).thenReturn(new Date(1_000L));
|
||||
BpmModelMetaInfoVO metaInfo = new BpmModelMetaInfoVO();
|
||||
metaInfo.setType(type);
|
||||
when(model.getMetaInfo()).thenReturn(JsonUtils.toJsonString(metaInfo));
|
||||
return model;
|
||||
}
|
||||
|
||||
private void mockGetModel(Model model) {
|
||||
when(repositoryService.createModelQuery()).thenReturn(modelQuery);
|
||||
when(modelQuery.modelId(eq(MODEL_ID))).thenReturn(modelQuery);
|
||||
when(modelQuery.modelTenantId(eq(TENANT_ID.toString()))).thenReturn(modelQuery);
|
||||
when(modelQuery.singleResult()).thenReturn(model);
|
||||
}
|
||||
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.performance;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.performance.CrmPerformanceConfigService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 业绩目标设置")
|
||||
@RestController
|
||||
@RequestMapping("/crm/performance-config")
|
||||
@Validated
|
||||
public class CrmPerformanceConfigController {
|
||||
|
||||
@Resource
|
||||
private CrmPerformanceConfigService performanceConfigService;
|
||||
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建业绩目标")
|
||||
@PreAuthorize("@ss.hasPermission('crm:performance-config:create')")
|
||||
public CommonResult<Long> createPerformanceConfig(@Valid @RequestBody CrmPerformanceConfigSaveReqVO createReqVO) {
|
||||
return success(performanceConfigService.createPerformanceConfig(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新业绩目标")
|
||||
@PreAuthorize("@ss.hasPermission('crm:performance-config:update')")
|
||||
public CommonResult<Boolean> updatePerformanceConfig(@Valid @RequestBody CrmPerformanceConfigSaveReqVO updateReqVO) {
|
||||
performanceConfigService.updatePerformanceConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除业绩目标")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('crm:performance-config:delete')")
|
||||
public CommonResult<Boolean> deletePerformanceConfig(@RequestParam("id") Long id) {
|
||||
performanceConfigService.deletePerformanceConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得业绩目标")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('crm:performance-config:query')")
|
||||
public CommonResult<CrmPerformanceConfigRespVO> getPerformanceConfig(@RequestParam("id") Long id) {
|
||||
// 1. 获取业绩目标
|
||||
CrmPerformanceConfigDO performanceConfig = performanceConfigService.getPerformanceConfig(id);
|
||||
// 2. 拼接数据
|
||||
return success(buildPerformanceConfigDetail(performanceConfig));
|
||||
}
|
||||
|
||||
private CrmPerformanceConfigRespVO buildPerformanceConfigDetail(CrmPerformanceConfigDO performanceConfig) {
|
||||
if (performanceConfig == null) {
|
||||
return null;
|
||||
}
|
||||
// 1. 转换成 VO
|
||||
CrmPerformanceConfigRespVO respVO = BeanUtils.toBean(performanceConfig, CrmPerformanceConfigRespVO.class);
|
||||
// 2. 拼接目标对象名称
|
||||
return buildPerformanceConfigObjectName(respVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得业绩目标分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:performance-config:query')")
|
||||
public CommonResult<PageResult<CrmPerformanceConfigRespVO>> getPerformanceConfigPage(@Valid CrmPerformanceConfigPageReqVO pageReqVO) {
|
||||
// 1. 查询业绩目标分页
|
||||
PageResult<CrmPerformanceConfigDO> pageResult = performanceConfigService.getPerformanceConfigPage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
// 2. 拼接数据
|
||||
return success(BeanUtils.toBean(pageResult, CrmPerformanceConfigRespVO.class)
|
||||
.setList(buildPerformanceConfigDetailList(pageResult.getList())));
|
||||
}
|
||||
|
||||
private List<CrmPerformanceConfigRespVO> buildPerformanceConfigDetailList(List<CrmPerformanceConfigDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return emptyList();
|
||||
}
|
||||
// 1. 转换成 VO
|
||||
List<CrmPerformanceConfigRespVO> voList = BeanUtils.toBean(list, CrmPerformanceConfigRespVO.class);
|
||||
// 2. 拼接目标对象名称
|
||||
return buildPerformanceConfigObjectNameList(voList);
|
||||
}
|
||||
|
||||
// ========== VO 拼装 ==========
|
||||
|
||||
private CrmPerformanceConfigRespVO buildPerformanceConfigObjectName(CrmPerformanceConfigRespVO config) {
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
return buildPerformanceConfigObjectNameList(singletonList(config)).get(0);
|
||||
}
|
||||
|
||||
private List<CrmPerformanceConfigRespVO> buildPerformanceConfigObjectNameList(List<CrmPerformanceConfigRespVO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return emptyList();
|
||||
}
|
||||
// 1.1 获取部门列表
|
||||
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(list, CrmPerformanceConfigRespVO::getObjectId,
|
||||
item -> Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())));
|
||||
// 1.2 获取员工列表
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(list, CrmPerformanceConfigRespVO::getObjectId,
|
||||
item -> Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())));
|
||||
// 2. 设置目标对象名称
|
||||
list.forEach(item -> {
|
||||
if (Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())) {
|
||||
findAndThen(deptMap, item.getObjectId(), dept -> item.setObjectName(dept.getName()));
|
||||
} else if (Objects.equals(item.getObjectType(), CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())) {
|
||||
findAndThen(userMap, item.getObjectId(), user -> item.setObjectName(user.getNickname()));
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标设置分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CrmPerformanceConfigPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "目标对象编号", example = "1")
|
||||
private Long objectId;
|
||||
|
||||
@Schema(description = "目标对象类型", example = "2")
|
||||
@InEnum(value = CrmPerformanceConfigObjectTypeEnum.class, message = "目标对象类型,必须是 {value}")
|
||||
private Integer objectType;
|
||||
|
||||
@Schema(description = "年份", example = "2026")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "目标类型", example = "5")
|
||||
@InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
|
||||
private Integer bizType;
|
||||
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标设置 Response VO")
|
||||
@Data
|
||||
public class CrmPerformanceConfigRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long objectId;
|
||||
|
||||
@Schema(description = "目标对象名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "销售部")
|
||||
private String objectName;
|
||||
|
||||
@Schema(description = "目标对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private Integer objectType;
|
||||
|
||||
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "一月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal januaryTargetPrice;
|
||||
|
||||
@Schema(description = "二月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal februaryTargetPrice;
|
||||
|
||||
@Schema(description = "三月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal marchTargetPrice;
|
||||
|
||||
@Schema(description = "四月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal aprilTargetPrice;
|
||||
|
||||
@Schema(description = "五月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal mayTargetPrice;
|
||||
|
||||
@Schema(description = "六月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal juneTargetPrice;
|
||||
|
||||
@Schema(description = "七月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal julyTargetPrice;
|
||||
|
||||
@Schema(description = "八月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal augustTargetPrice;
|
||||
|
||||
@Schema(description = "九月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal septemberTargetPrice;
|
||||
|
||||
@Schema(description = "十月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal octoberTargetPrice;
|
||||
|
||||
@Schema(description = "十一月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal novemberTargetPrice;
|
||||
|
||||
@Schema(description = "十二月目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal decemberTargetPrice;
|
||||
|
||||
@Schema(description = "目标类型",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
private Integer bizType;
|
||||
|
||||
@Schema(description = "年度目标金额", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private BigDecimal yearTargetPrice;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标设置新增/修改 Request VO")
|
||||
@Data
|
||||
public class CrmPerformanceConfigSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标对象编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "目标对象编号不能为空")
|
||||
private Long objectId;
|
||||
|
||||
@Schema(description = "目标对象类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
@NotNull(message = "目标对象类型不能为空")
|
||||
@InEnum(value = CrmPerformanceConfigObjectTypeEnum.class, message = "目标对象类型,必须是 {value}")
|
||||
private Integer objectType;
|
||||
|
||||
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
|
||||
@NotNull(message = "年份不能为空")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "一月目标金额")
|
||||
private BigDecimal januaryTargetPrice;
|
||||
|
||||
@Schema(description = "二月目标金额")
|
||||
private BigDecimal februaryTargetPrice;
|
||||
|
||||
@Schema(description = "三月目标金额")
|
||||
private BigDecimal marchTargetPrice;
|
||||
|
||||
@Schema(description = "四月目标金额")
|
||||
private BigDecimal aprilTargetPrice;
|
||||
|
||||
@Schema(description = "五月目标金额")
|
||||
private BigDecimal mayTargetPrice;
|
||||
|
||||
@Schema(description = "六月目标金额")
|
||||
private BigDecimal juneTargetPrice;
|
||||
|
||||
@Schema(description = "七月目标金额")
|
||||
private BigDecimal julyTargetPrice;
|
||||
|
||||
@Schema(description = "八月目标金额")
|
||||
private BigDecimal augustTargetPrice;
|
||||
|
||||
@Schema(description = "九月目标金额")
|
||||
private BigDecimal septemberTargetPrice;
|
||||
|
||||
@Schema(description = "十月目标金额")
|
||||
private BigDecimal octoberTargetPrice;
|
||||
|
||||
@Schema(description = "十一月目标金额")
|
||||
private BigDecimal novemberTargetPrice;
|
||||
|
||||
@Schema(description = "十二月目标金额")
|
||||
private BigDecimal decemberTargetPrice;
|
||||
|
||||
@Schema(description = "目标类型",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "目标类型不能为空")
|
||||
@InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
|
||||
private Integer bizType;
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPerformanceTargetService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 业绩目标统计")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-performance-target")
|
||||
@Validated
|
||||
public class CrmStatisticsPerformanceTargetController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPerformanceTargetService performanceTargetService;
|
||||
|
||||
@GetMapping("/get-performance-target-summary")
|
||||
@Operation(summary = "获取业绩目标完成情况")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-performance-target:query')")
|
||||
public CommonResult<List<CrmStatisticsPerformanceTargetRespVO>> getPerformanceTargetSummary(@Valid CrmStatisticsPerformanceTargetReqVO reqVO) {
|
||||
return success(performanceTargetService.getPerformanceTargetSummary(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
|
||||
import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsProductService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - CRM 产品统计")
|
||||
@RestController
|
||||
@RequestMapping("/crm/statistics-product")
|
||||
@Validated
|
||||
public class CrmStatisticsProductController {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsProductService productService;
|
||||
|
||||
@GetMapping("/get-product-sales-list")
|
||||
@Operation(summary = "获取产品销售情况统计")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-product:query')")
|
||||
public CommonResult<List<CrmStatisticsProductSalesRespVO>> getProductSalesList(@Valid CrmStatisticsProductReqVO reqVO) {
|
||||
return success(productService.getProductSalesList(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/get-product-category-summary")
|
||||
@Operation(summary = "获取产品分类销售分析")
|
||||
@PreAuthorize("@ss.hasPermission('crm:statistics-product:query')")
|
||||
public CommonResult<List<CrmStatisticsProductCategoryRespVO>> getProductCategorySummary(@Valid CrmStatisticsProductReqVO reqVO) {
|
||||
return success(productService.getProductCategorySummary(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.funnel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 商机阶段统计 Response VO")
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class CrmStatisticsBusinessSummaryByStatusRespVO {
|
||||
|
||||
@Schema(description = "商机阶段编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long statusId;
|
||||
|
||||
@Schema(description = "商机阶段名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "初步接洽")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "赢单率,百分比", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
|
||||
private Integer statusPercent;
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "商机数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long businessCount;
|
||||
|
||||
@Schema(description = "商机总金额,单位:元", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 员工业绩合同汇总 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsPerformanceSummaryRespVO {
|
||||
|
||||
@Schema(description = "时间轴", requiredMode = Schema.RequiredMode.REQUIRED, example = "202401")
|
||||
private String time;
|
||||
|
||||
@Schema(description = "合同数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer contractCount;
|
||||
|
||||
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
|
||||
private BigDecimal contractPrice;
|
||||
|
||||
@Schema(description = "回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
|
||||
private BigDecimal receivablePrice;
|
||||
|
||||
@Schema(description = "未回款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000.00")
|
||||
private BigDecimal unreceivedPrice;
|
||||
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标完成实际值 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class CrmStatisticsPerformanceTargetActualRespVO {
|
||||
|
||||
@Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "完成金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
|
||||
private BigDecimal currentPrice;
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标完成情况 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsPerformanceTargetReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024")
|
||||
@NotNull(message = "年份不能为空")
|
||||
private Integer year;
|
||||
|
||||
@Schema(description = "目标类型",
|
||||
requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||
@NotNull(message = "目标类型不能为空")
|
||||
@InEnum(value = CrmPerformanceConfigBizTypeEnum.class, message = "目标类型,必须是 {value}")
|
||||
private Integer bizType;
|
||||
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 业绩目标完成情况 Response VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class CrmStatisticsPerformanceTargetRespVO {
|
||||
|
||||
@Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer month;
|
||||
|
||||
@Schema(description = "目标金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
|
||||
private BigDecimal targetPrice;
|
||||
|
||||
@Schema(description = "完成金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000.00")
|
||||
private BigDecimal currentPrice;
|
||||
|
||||
@Schema(description = "完成率", requiredMode = Schema.RequiredMode.REQUIRED, example = "80.00")
|
||||
private BigDecimal completionRate;
|
||||
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 产品分类销售分析 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsProductCategoryRespVO {
|
||||
|
||||
@Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "合同数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||
private Integer contractCount;
|
||||
|
||||
@Schema(description = "销售数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private BigDecimal productCount;
|
||||
|
||||
@Schema(description = "销售金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "20000.00")
|
||||
private BigDecimal productTotalPrice;
|
||||
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 产品分析 Request VO")
|
||||
@Data
|
||||
public class CrmStatisticsProductReqVO {
|
||||
|
||||
@Schema(description = "部门 id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "部门 id 不能为空")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "负责人用户 id", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "负责人用户 id 集合", hidden = true, example = "2")
|
||||
private List<Long> userIds;
|
||||
|
||||
@Schema(description = "时间范围", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@NotEmpty(message = "请选择时间范围")
|
||||
@Size(min = 2, max = 2, message = "请选择时间范围")
|
||||
private LocalDateTime[] times;
|
||||
|
||||
@Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "10")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "产品分类编号集合", hidden = true, example = "10")
|
||||
private List<Long> categoryIds;
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "20")
|
||||
private Long productId;
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Schema(description = "管理后台 - CRM 产品销售情况 Response VO")
|
||||
@Data
|
||||
public class CrmStatisticsProductSalesRespVO {
|
||||
|
||||
@Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Long categoryId;
|
||||
|
||||
@Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "软件")
|
||||
private String categoryName;
|
||||
|
||||
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
|
||||
private Long productId;
|
||||
|
||||
@Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CRM 专业版")
|
||||
private String productName;
|
||||
|
||||
@Schema(description = "合同编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
|
||||
private Long contractId;
|
||||
|
||||
@Schema(description = "合同编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "HT20240101001")
|
||||
private String contractNo;
|
||||
|
||||
@Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CRM 采购合同")
|
||||
private String contractName;
|
||||
|
||||
@Schema(description = "负责人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "负责人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||
private String ownerUserName;
|
||||
|
||||
@Schema(description = "客户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "40")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "客户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试客户")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
|
||||
private BigDecimal productPrice;
|
||||
|
||||
@Schema(description = "销售数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||
private BigDecimal productCount;
|
||||
|
||||
@Schema(description = "销售金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "200.00")
|
||||
private BigDecimal productTotalPrice;
|
||||
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.dataobject.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("crm_performance_config")
|
||||
@KeySequence("crm_performance_config_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CrmPerformanceConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 目标类型
|
||||
*
|
||||
* 枚举 {@link CrmPerformanceConfigBizTypeEnum}
|
||||
*/
|
||||
private Integer bizType;
|
||||
/**
|
||||
* 目标对象编号
|
||||
*/
|
||||
private Long objectId;
|
||||
/**
|
||||
* 目标对象类型
|
||||
*
|
||||
* 枚举 {@link CrmPerformanceConfigObjectTypeEnum}
|
||||
*/
|
||||
private Integer objectType;
|
||||
|
||||
/**
|
||||
* 年份
|
||||
*/
|
||||
private Integer year;
|
||||
/**
|
||||
* 年度目标金额
|
||||
*/
|
||||
private BigDecimal yearTargetPrice;
|
||||
/**
|
||||
* 一月目标金额
|
||||
*/
|
||||
private BigDecimal januaryTargetPrice;
|
||||
/**
|
||||
* 二月目标金额
|
||||
*/
|
||||
private BigDecimal februaryTargetPrice;
|
||||
/**
|
||||
* 三月目标金额
|
||||
*/
|
||||
private BigDecimal marchTargetPrice;
|
||||
/**
|
||||
* 四月目标金额
|
||||
*/
|
||||
private BigDecimal aprilTargetPrice;
|
||||
/**
|
||||
* 五月目标金额
|
||||
*/
|
||||
private BigDecimal mayTargetPrice;
|
||||
/**
|
||||
* 六月目标金额
|
||||
*/
|
||||
private BigDecimal juneTargetPrice;
|
||||
/**
|
||||
* 七月目标金额
|
||||
*/
|
||||
private BigDecimal julyTargetPrice;
|
||||
/**
|
||||
* 八月目标金额
|
||||
*/
|
||||
private BigDecimal augustTargetPrice;
|
||||
/**
|
||||
* 九月目标金额
|
||||
*/
|
||||
private BigDecimal septemberTargetPrice;
|
||||
/**
|
||||
* 十月目标金额
|
||||
*/
|
||||
private BigDecimal octoberTargetPrice;
|
||||
/**
|
||||
* 十一月目标金额
|
||||
*/
|
||||
private BigDecimal novemberTargetPrice;
|
||||
/**
|
||||
* 十二月目标金额
|
||||
*/
|
||||
private BigDecimal decemberTargetPrice;
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.dataobject.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* CRM 负责人变更记录 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("crm_owner_record")
|
||||
@KeySequence("crm_owner_record_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CrmOwnerRecordDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* CRM 业务类型
|
||||
*
|
||||
* 枚举 {@link CrmBizTypeEnum}
|
||||
*/
|
||||
private Integer bizType;
|
||||
/**
|
||||
* CRM 业务编号
|
||||
*/
|
||||
private Long bizId;
|
||||
/**
|
||||
* 变更前负责人
|
||||
*/
|
||||
private Long preOwnerUserId;
|
||||
/**
|
||||
* 变更后负责人
|
||||
*/
|
||||
private Long postOwnerUserId;
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标配置 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmPerformanceConfigMapper extends BaseMapperX<CrmPerformanceConfigDO> {
|
||||
|
||||
default PageResult<CrmPerformanceConfigDO> selectPage(CrmPerformanceConfigPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
|
||||
.eqIfPresent(CrmPerformanceConfigDO::getObjectId, reqVO.getObjectId())
|
||||
.eqIfPresent(CrmPerformanceConfigDO::getObjectType, reqVO.getObjectType())
|
||||
.eqIfPresent(CrmPerformanceConfigDO::getYear, reqVO.getYear())
|
||||
.eqIfPresent(CrmPerformanceConfigDO::getBizType, reqVO.getBizType())
|
||||
.orderByDesc(CrmPerformanceConfigDO::getYear)
|
||||
.orderByAsc(CrmPerformanceConfigDO::getObjectType)
|
||||
.orderByAsc(CrmPerformanceConfigDO::getObjectId)
|
||||
.orderByAsc(CrmPerformanceConfigDO::getBizType));
|
||||
}
|
||||
|
||||
default CrmPerformanceConfigDO selectByObjectIdAndObjectTypeAndYearAndBizType(Long objectId, Integer objectType,
|
||||
Integer year, Integer bizType) {
|
||||
return selectOne(new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
|
||||
.eq(CrmPerformanceConfigDO::getObjectId, objectId)
|
||||
.eq(CrmPerformanceConfigDO::getObjectType, objectType)
|
||||
.eq(CrmPerformanceConfigDO::getYear, year)
|
||||
.eq(CrmPerformanceConfigDO::getBizType, bizType));
|
||||
}
|
||||
|
||||
default List<CrmPerformanceConfigDO> selectListByObjectIdsAndObjectTypeAndYearAndBizType(Collection<Long> objectIds,
|
||||
Integer objectType,
|
||||
Integer year,
|
||||
Integer bizType) {
|
||||
return selectList(new LambdaQueryWrapperX<CrmPerformanceConfigDO>()
|
||||
.in(CrmPerformanceConfigDO::getObjectId, objectIds)
|
||||
.eq(CrmPerformanceConfigDO::getObjectType, objectType)
|
||||
.eq(CrmPerformanceConfigDO::getYear, year)
|
||||
.eq(CrmPerformanceConfigDO::getBizType, bizType));
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmOwnerRecordDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 负责人变更记录 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmOwnerRecordMapper extends BaseMapperX<CrmOwnerRecordDO> {
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> selectPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> selectPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标完成情况 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsPerformanceTargetMapper {
|
||||
|
||||
/**
|
||||
* 按月统计合同金额
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 按月统计合同金额
|
||||
*/
|
||||
List<CrmStatisticsPerformanceTargetActualRespVO> selectContractActualByMonth(CrmStatisticsPerformanceTargetReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 按月统计回款金额
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 按月统计回款金额
|
||||
*/
|
||||
List<CrmStatisticsPerformanceTargetActualRespVO> selectReceivableActualByMonth(CrmStatisticsPerformanceTargetReqVO reqVO);
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 产品分析 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface CrmStatisticsProductMapper {
|
||||
|
||||
/**
|
||||
* 获取产品销售情况统计
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 产品销售情况
|
||||
*/
|
||||
List<CrmStatisticsProductSalesRespVO> selectProductSalesList(CrmStatisticsProductReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取产品分类销售分析
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 产品分类销售分析
|
||||
*/
|
||||
List<CrmStatisticsProductCategoryRespVO> selectProductCategorySummary(CrmStatisticsProductReqVO reqVO);
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.enums.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标业务类型枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CrmPerformanceConfigBizTypeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
CRM_CONTRACT(CrmBizTypeEnum.CRM_CONTRACT.getType(), "销售目标"),
|
||||
CRM_RECEIVABLE(CrmBizTypeEnum.CRM_RECEIVABLE.getType(), "回款目标");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values())
|
||||
.map(CrmPerformanceConfigBizTypeEnum::getBizType).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private final Integer bizType;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.enums.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标对象类型枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CrmPerformanceConfigObjectTypeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
DEPT(2, "部门"),
|
||||
USER(3, "员工");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values())
|
||||
.map(CrmPerformanceConfigObjectTypeEnum::getObjectType).toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 对象类型
|
||||
*/
|
||||
private final Integer objectType;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标设置 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CrmPerformanceConfigService {
|
||||
|
||||
/**
|
||||
* 创建业绩目标
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createPerformanceConfig(@Valid CrmPerformanceConfigSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新业绩目标
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updatePerformanceConfig(@Valid CrmPerformanceConfigSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除业绩目标
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deletePerformanceConfig(Long id);
|
||||
|
||||
/**
|
||||
* 获得业绩目标
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 业绩目标
|
||||
*/
|
||||
CrmPerformanceConfigDO getPerformanceConfig(Long id);
|
||||
|
||||
/**
|
||||
* 获得业绩目标分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 业绩目标分页
|
||||
*/
|
||||
PageResult<CrmPerformanceConfigDO> getPerformanceConfigPage(CrmPerformanceConfigPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 根据对象、年份、目标类型获得业绩目标配置
|
||||
*
|
||||
* @param objectId 对象编号
|
||||
* @param objectType 对象类型
|
||||
* @param year 年份
|
||||
* @param bizType 业务类型
|
||||
* @return 业绩目标配置
|
||||
*/
|
||||
CrmPerformanceConfigDO getPerformanceConfig(Long objectId, Integer objectType, Integer year, Integer bizType);
|
||||
|
||||
/**
|
||||
* 根据对象数组、年份、目标类型获得业绩目标配置列表
|
||||
*
|
||||
* @param objectIds 对象编号数组
|
||||
* @param objectType 对象类型
|
||||
* @param year 年份
|
||||
* @param bizType 业务类型
|
||||
* @return 业绩目标配置列表
|
||||
*/
|
||||
List<CrmPerformanceConfigDO> getPerformanceConfigList(Collection<Long> objectIds, Integer objectType, Integer year,
|
||||
Integer bizType);
|
||||
|
||||
}
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.performance;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.performance.vo.config.CrmPerformanceConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.performance.CrmPerformanceConfigMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.hutool.core.collection.CollUtil.isEmpty;
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标设置 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmPerformanceConfigServiceImpl implements CrmPerformanceConfigService {
|
||||
|
||||
@Resource
|
||||
private CrmPerformanceConfigMapper performanceConfigMapper;
|
||||
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
public Long createPerformanceConfig(CrmPerformanceConfigSaveReqVO createReqVO) {
|
||||
// 1.1 校验目标对象
|
||||
validatePerformanceConfigObject(createReqVO.getObjectType(), createReqVO.getObjectId());
|
||||
// 1.2 校验唯一
|
||||
validatePerformanceConfigNotExists(null, createReqVO);
|
||||
|
||||
// 2. 插入
|
||||
CrmPerformanceConfigDO performanceConfig = BeanUtils.toBean(createReqVO, CrmPerformanceConfigDO.class);
|
||||
fillTargetPrice(performanceConfig);
|
||||
performanceConfigMapper.insert(performanceConfig);
|
||||
return performanceConfig.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePerformanceConfig(CrmPerformanceConfigSaveReqVO updateReqVO) {
|
||||
// 1.1 校验存在
|
||||
validatePerformanceConfigExists(updateReqVO.getId());
|
||||
// 1.2 校验目标对象
|
||||
validatePerformanceConfigObject(updateReqVO.getObjectType(), updateReqVO.getObjectId());
|
||||
// 1.3 校验唯一
|
||||
validatePerformanceConfigNotExists(updateReqVO.getId(), updateReqVO);
|
||||
|
||||
// 2. 更新
|
||||
CrmPerformanceConfigDO updateObj = BeanUtils.toBean(updateReqVO, CrmPerformanceConfigDO.class);
|
||||
fillTargetPrice(updateObj);
|
||||
performanceConfigMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePerformanceConfig(Long id) {
|
||||
// 1. 校验存在
|
||||
validatePerformanceConfigExists(id);
|
||||
// 2. 删除
|
||||
performanceConfigMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmPerformanceConfigDO getPerformanceConfig(Long id) {
|
||||
return validatePerformanceConfigExists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CrmPerformanceConfigDO> getPerformanceConfigPage(CrmPerformanceConfigPageReqVO pageReqVO) {
|
||||
return performanceConfigMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
private CrmPerformanceConfigDO validatePerformanceConfigExists(Long id) {
|
||||
CrmPerformanceConfigDO performanceConfig = performanceConfigMapper.selectById(id);
|
||||
if (performanceConfig == null) {
|
||||
throw exception(PERFORMANCE_CONFIG_NOT_EXISTS);
|
||||
}
|
||||
return performanceConfig;
|
||||
}
|
||||
|
||||
private void validatePerformanceConfigNotExists(Long id, CrmPerformanceConfigSaveReqVO reqVO) {
|
||||
CrmPerformanceConfigDO performanceConfig = performanceConfigMapper.selectByObjectIdAndObjectTypeAndYearAndBizType(
|
||||
reqVO.getObjectId(), reqVO.getObjectType(), reqVO.getYear(), reqVO.getBizType());
|
||||
if (performanceConfig == null || Objects.equals(performanceConfig.getId(), id)) {
|
||||
return;
|
||||
}
|
||||
throw exception(PERFORMANCE_CONFIG_EXISTS);
|
||||
}
|
||||
|
||||
private void validatePerformanceConfigObject(Integer objectType, Long objectId) {
|
||||
// 情况一:部门
|
||||
if (Objects.equals(objectType, CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType())) {
|
||||
deptApi.validateDeptList(singleton(objectId));
|
||||
return;
|
||||
}
|
||||
// 情况二:员工
|
||||
if (Objects.equals(objectType, CrmPerformanceConfigObjectTypeEnum.USER.getObjectType())) {
|
||||
adminUserApi.validateUserList(singleton(objectId));
|
||||
return;
|
||||
}
|
||||
throw exception(PERFORMANCE_CONFIG_TYPE_INVALID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrmPerformanceConfigDO getPerformanceConfig(Long objectId, Integer objectType, Integer year, Integer bizType) {
|
||||
return performanceConfigMapper.selectByObjectIdAndObjectTypeAndYearAndBizType(objectId, objectType, year, bizType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmPerformanceConfigDO> getPerformanceConfigList(Collection<Long> objectIds, Integer objectType,
|
||||
Integer year, Integer bizType) {
|
||||
if (isEmpty(objectIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return performanceConfigMapper.selectListByObjectIdsAndObjectTypeAndYearAndBizType(objectIds, objectType,
|
||||
year, bizType);
|
||||
}
|
||||
|
||||
private void fillTargetPrice(CrmPerformanceConfigDO performanceConfig) {
|
||||
// 1. 空值设置为 0
|
||||
performanceConfig.setJanuaryTargetPrice(defaultIfNull(performanceConfig.getJanuaryTargetPrice()));
|
||||
performanceConfig.setFebruaryTargetPrice(defaultIfNull(performanceConfig.getFebruaryTargetPrice()));
|
||||
performanceConfig.setMarchTargetPrice(defaultIfNull(performanceConfig.getMarchTargetPrice()));
|
||||
performanceConfig.setAprilTargetPrice(defaultIfNull(performanceConfig.getAprilTargetPrice()));
|
||||
performanceConfig.setMayTargetPrice(defaultIfNull(performanceConfig.getMayTargetPrice()));
|
||||
performanceConfig.setJuneTargetPrice(defaultIfNull(performanceConfig.getJuneTargetPrice()));
|
||||
performanceConfig.setJulyTargetPrice(defaultIfNull(performanceConfig.getJulyTargetPrice()));
|
||||
performanceConfig.setAugustTargetPrice(defaultIfNull(performanceConfig.getAugustTargetPrice()));
|
||||
performanceConfig.setSeptemberTargetPrice(defaultIfNull(performanceConfig.getSeptemberTargetPrice()));
|
||||
performanceConfig.setOctoberTargetPrice(defaultIfNull(performanceConfig.getOctoberTargetPrice()));
|
||||
performanceConfig.setNovemberTargetPrice(defaultIfNull(performanceConfig.getNovemberTargetPrice()));
|
||||
performanceConfig.setDecemberTargetPrice(defaultIfNull(performanceConfig.getDecemberTargetPrice()));
|
||||
// 2. 计算年度目标
|
||||
performanceConfig.setYearTargetPrice(performanceConfig.getJanuaryTargetPrice()
|
||||
.add(performanceConfig.getFebruaryTargetPrice())
|
||||
.add(performanceConfig.getMarchTargetPrice())
|
||||
.add(performanceConfig.getAprilTargetPrice())
|
||||
.add(performanceConfig.getMayTargetPrice())
|
||||
.add(performanceConfig.getJuneTargetPrice())
|
||||
.add(performanceConfig.getJulyTargetPrice())
|
||||
.add(performanceConfig.getAugustTargetPrice())
|
||||
.add(performanceConfig.getSeptemberTargetPrice())
|
||||
.add(performanceConfig.getOctoberTargetPrice())
|
||||
.add(performanceConfig.getNovemberTargetPrice())
|
||||
.add(performanceConfig.getDecemberTargetPrice()));
|
||||
}
|
||||
|
||||
private BigDecimal defaultIfNull(BigDecimal value) {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.permission;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmOwnerRecordCreateReqBO;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 负责人变更记录 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CrmOwnerRecordService {
|
||||
|
||||
/**
|
||||
* 创建负责人变更记录
|
||||
*
|
||||
* @param createReqBO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createOwnerRecord(@Valid CrmOwnerRecordCreateReqBO createReqBO);
|
||||
|
||||
/**
|
||||
* 批量创建负责人变更记录
|
||||
*
|
||||
* @param createReqBOs 创建信息
|
||||
*/
|
||||
void createOwnerRecordList(@Valid List<CrmOwnerRecordCreateReqBO> createReqBOs);
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按日期)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 进入公海客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 公海领取客户数(按用户)
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 统计数据
|
||||
*/
|
||||
List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO);
|
||||
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.permission;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmOwnerRecordDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmOwnerRecordMapper;
|
||||
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmOwnerRecordCreateReqBO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 负责人变更记录 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmOwnerRecordServiceImpl implements CrmOwnerRecordService {
|
||||
|
||||
@Resource
|
||||
private CrmOwnerRecordMapper ownerRecordMapper;
|
||||
|
||||
@Override
|
||||
public Long createOwnerRecord(CrmOwnerRecordCreateReqBO createReqBO) {
|
||||
CrmOwnerRecordDO ownerRecord = BeanUtils.toBean(createReqBO, CrmOwnerRecordDO.class);
|
||||
ownerRecordMapper.insert(ownerRecord);
|
||||
return ownerRecord.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOwnerRecordList(List<CrmOwnerRecordCreateReqBO> createReqBOs) {
|
||||
if (CollUtil.isEmpty(createReqBOs)) {
|
||||
return;
|
||||
}
|
||||
List<CrmOwnerRecordDO> list = BeanUtils.toBean(createReqBOs, CrmOwnerRecordDO.class);
|
||||
ownerRecordMapper.insertBatch(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerPutCountByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return ownerRecordMapper.selectPoolCustomerPutCountByDate(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByDateRespVO> getPoolCustomerTakeCountByDate(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return ownerRecordMapper.selectPoolCustomerTakeCountByDate(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerPutCountByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return ownerRecordMapper.selectPoolCustomerPutCountByUser(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPoolSummaryByUserRespVO> getPoolCustomerTakeCountByUser(CrmStatisticsCustomerReqVO reqVO) {
|
||||
return ownerRecordMapper.selectPoolCustomerTakeCountByUser(reqVO);
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.permission.bo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* CRM 负责人变更记录 Create Req BO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class CrmOwnerRecordCreateReqBO {
|
||||
|
||||
/**
|
||||
* CRM 业务类型
|
||||
*/
|
||||
@NotNull(message = "CRM 业务类型不能为空")
|
||||
@InEnum(CrmBizTypeEnum.class)
|
||||
private Integer bizType;
|
||||
|
||||
/**
|
||||
* CRM 业务编号
|
||||
*/
|
||||
@NotNull(message = "CRM 业务编号不能为空")
|
||||
private Long bizId;
|
||||
|
||||
/**
|
||||
* 变更前负责人
|
||||
*/
|
||||
private Long preOwnerUserId;
|
||||
|
||||
/**
|
||||
* 变更后负责人
|
||||
*/
|
||||
private Long postOwnerUserId;
|
||||
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标统计 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CrmStatisticsPerformanceTargetService {
|
||||
|
||||
/**
|
||||
* 获取业绩目标完成情况
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 业绩目标完成情况
|
||||
*/
|
||||
List<CrmStatisticsPerformanceTargetRespVO> getPerformanceTargetSummary(CrmStatisticsPerformanceTargetReqVO reqVO);
|
||||
|
||||
}
|
||||
-190
@@ -1,190 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.performance.CrmPerformanceConfigDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceTargetMapper;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigBizTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.enums.performance.CrmPerformanceConfigObjectTypeEnum;
|
||||
import cn.iocoder.yudao.module.crm.service.performance.CrmPerformanceConfigService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
|
||||
/**
|
||||
* CRM 业绩目标统计 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsPerformanceTargetServiceImpl implements CrmStatisticsPerformanceTargetService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsPerformanceTargetMapper performanceTargetMapper;
|
||||
@Resource
|
||||
private CrmPerformanceConfigService performanceConfigService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsPerformanceTargetRespVO> getPerformanceTargetSummary(CrmStatisticsPerformanceTargetReqVO reqVO) {
|
||||
// 1. 获得用户编号数组
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2.1 获得目标配置
|
||||
List<CrmPerformanceConfigDO> performanceConfigs = getPerformanceConfigs(reqVO);
|
||||
// 2.2 获得实际完成金额
|
||||
List<CrmStatisticsPerformanceTargetActualRespVO> actualList = ObjUtil.equal(reqVO.getBizType(),
|
||||
CrmPerformanceConfigBizTypeEnum.CRM_RECEIVABLE.getBizType())
|
||||
? performanceTargetMapper.selectReceivableActualByMonth(reqVO)
|
||||
: performanceTargetMapper.selectContractActualByMonth(reqVO);
|
||||
Map<Integer, BigDecimal> actualMap = convertMap(actualList,
|
||||
CrmStatisticsPerformanceTargetActualRespVO::getMonth,
|
||||
CrmStatisticsPerformanceTargetActualRespVO::getCurrentPrice);
|
||||
|
||||
// 3. 组装数据返回
|
||||
List<CrmStatisticsPerformanceTargetRespVO> result = new ArrayList<>();
|
||||
for (int month = 1; month <= 12; month++) {
|
||||
BigDecimal targetPrice = getMonthTargetPrice(performanceConfigs, month);
|
||||
BigDecimal currentPrice = actualMap.getOrDefault(month, BigDecimal.ZERO);
|
||||
result.add(new CrmStatisticsPerformanceTargetRespVO().setMonth(month).setTargetPrice(targetPrice)
|
||||
.setCurrentPrice(currentPrice)
|
||||
.setCompletionRate(calculateCompletionRate(currentPrice, targetPrice)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<CrmPerformanceConfigDO> getPerformanceConfigs(CrmStatisticsPerformanceTargetReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
CrmPerformanceConfigDO performanceConfig = performanceConfigService.getPerformanceConfig(reqVO.getUserId(),
|
||||
CrmPerformanceConfigObjectTypeEnum.USER.getObjectType(), reqVO.getYear(), reqVO.getBizType());
|
||||
return performanceConfig == null ? Collections.emptyList() : ListUtil.of(performanceConfig);
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
return performanceConfigService.getPerformanceConfigList(getDeptIds(reqVO.getDeptId()),
|
||||
CrmPerformanceConfigObjectTypeEnum.DEPT.getObjectType(), reqVO.getYear(), reqVO.getBizType());
|
||||
}
|
||||
|
||||
private BigDecimal getMonthTargetPrice(List<CrmPerformanceConfigDO> performanceConfigs, int month) {
|
||||
return performanceConfigs.stream()
|
||||
.map(performanceConfig -> getMonthTargetPrice(performanceConfig, month))
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnhancedSwitchMigration")
|
||||
private BigDecimal getMonthTargetPrice(CrmPerformanceConfigDO performanceConfig, int month) {
|
||||
// 1. 无目标配置,返回 0
|
||||
if (performanceConfig == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
// 2. 获得月份目标
|
||||
BigDecimal targetPrice;
|
||||
switch (month) {
|
||||
case 1:
|
||||
targetPrice = performanceConfig.getJanuaryTargetPrice();
|
||||
break;
|
||||
case 2:
|
||||
targetPrice = performanceConfig.getFebruaryTargetPrice();
|
||||
break;
|
||||
case 3:
|
||||
targetPrice = performanceConfig.getMarchTargetPrice();
|
||||
break;
|
||||
case 4:
|
||||
targetPrice = performanceConfig.getAprilTargetPrice();
|
||||
break;
|
||||
case 5:
|
||||
targetPrice = performanceConfig.getMayTargetPrice();
|
||||
break;
|
||||
case 6:
|
||||
targetPrice = performanceConfig.getJuneTargetPrice();
|
||||
break;
|
||||
case 7:
|
||||
targetPrice = performanceConfig.getJulyTargetPrice();
|
||||
break;
|
||||
case 8:
|
||||
targetPrice = performanceConfig.getAugustTargetPrice();
|
||||
break;
|
||||
case 9:
|
||||
targetPrice = performanceConfig.getSeptemberTargetPrice();
|
||||
break;
|
||||
case 10:
|
||||
targetPrice = performanceConfig.getOctoberTargetPrice();
|
||||
break;
|
||||
case 11:
|
||||
targetPrice = performanceConfig.getNovemberTargetPrice();
|
||||
break;
|
||||
case 12:
|
||||
targetPrice = performanceConfig.getDecemberTargetPrice();
|
||||
break;
|
||||
default:
|
||||
targetPrice = BigDecimal.ZERO;
|
||||
}
|
||||
return targetPrice != null ? targetPrice : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
private BigDecimal calculateCompletionRate(BigDecimal currentPrice, BigDecimal targetPrice) {
|
||||
if (targetPrice == null || targetPrice.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
return currentPrice.multiply(BigDecimal.valueOf(100)).divide(targetPrice, 2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号数组
|
||||
*
|
||||
* 如果用户编号为空,则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
private List<Long> getUserIds(CrmStatisticsPerformanceTargetReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
return ListUtil.of(reqVO.getUserId());
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
// 2.1 获得部门列表
|
||||
List<Long> deptIds = getDeptIds(reqVO.getDeptId());
|
||||
// 2.2 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得部门编号数组,包括子部门
|
||||
*
|
||||
* @param deptId 部门编号
|
||||
* @return 部门编号数组
|
||||
*/
|
||||
private List<Long> getDeptIds(Long deptId) {
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(deptId), DeptRespDTO::getId);
|
||||
deptIds.add(deptId);
|
||||
return deptIds;
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* CRM 产品分析 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface CrmStatisticsProductService {
|
||||
|
||||
/**
|
||||
* 获取产品销售情况统计
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 产品销售情况
|
||||
*/
|
||||
List<CrmStatisticsProductSalesRespVO> getProductSalesList(CrmStatisticsProductReqVO reqVO);
|
||||
|
||||
/**
|
||||
* 获取产品分类销售分析
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 产品分类销售分析
|
||||
*/
|
||||
List<CrmStatisticsProductCategoryRespVO> getProductCategorySummary(CrmStatisticsProductReqVO reqVO);
|
||||
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
package cn.iocoder.yudao.module.crm.service.statistics;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.category.CrmProductCategoryListReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductReqVO;
|
||||
import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO;
|
||||
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO;
|
||||
import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsProductMapper;
|
||||
import cn.iocoder.yudao.module.crm.service.product.CrmProductCategoryService;
|
||||
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
|
||||
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
|
||||
/**
|
||||
* CRM 产品分析 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmStatisticsProductServiceImpl implements CrmStatisticsProductService {
|
||||
|
||||
@Resource
|
||||
private CrmStatisticsProductMapper productMapper;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
@Resource
|
||||
private DeptApi deptApi;
|
||||
@Resource
|
||||
private CrmProductCategoryService productCategoryService;
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsProductSalesRespVO> getProductSalesList(CrmStatisticsProductReqVO reqVO) {
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
setCategoryIds(reqVO);
|
||||
return productMapper.selectProductSalesList(reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CrmStatisticsProductCategoryRespVO> getProductCategorySummary(CrmStatisticsProductReqVO reqVO) {
|
||||
reqVO.setUserIds(getUserIds(reqVO));
|
||||
if (CollUtil.isEmpty(reqVO.getUserIds())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
setCategoryIds(reqVO);
|
||||
return productMapper.selectProductCategorySummary(reqVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置产品分类编号数组,选择父分类时,包含子分类
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
*/
|
||||
private void setCategoryIds(CrmStatisticsProductReqVO reqVO) {
|
||||
if (ObjUtil.isNull(reqVO.getCategoryId())) {
|
||||
return;
|
||||
}
|
||||
List<Long> categoryIds = ListUtil.of(reqVO.getCategoryId());
|
||||
List<CrmProductCategoryDO> categoryList = productCategoryService.getProductCategoryList(
|
||||
new CrmProductCategoryListReqVO());
|
||||
categoryIds.addAll(convertList(categoryList, CrmProductCategoryDO::getId,
|
||||
category -> Objects.equals(category.getParentId(), reqVO.getCategoryId())));
|
||||
reqVO.setCategoryIds(categoryIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户编号数组
|
||||
*
|
||||
* 如果用户编号为空,则获得部门下的用户编号数组,包括子部门的所有用户编号
|
||||
*
|
||||
* @param reqVO 请求参数
|
||||
* @return 用户编号数组
|
||||
*/
|
||||
private List<Long> getUserIds(CrmStatisticsProductReqVO reqVO) {
|
||||
// 情况一:选中某个用户
|
||||
if (ObjUtil.isNotNull(reqVO.getUserId())) {
|
||||
return ListUtil.of(reqVO.getUserId());
|
||||
}
|
||||
// 情况二:选中某个部门
|
||||
// 2.1 获得部门列表
|
||||
List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
|
||||
deptIds.add(reqVO.getDeptId());
|
||||
// 2.2 获得用户编号
|
||||
return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?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="cn.iocoder.yudao.module.crm.dal.mysql.permission.CrmOwnerRecordMapper">
|
||||
|
||||
<select id="selectPoolCustomerPutCountByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(*) AS customer_put_count
|
||||
FROM crm_owner_record
|
||||
WHERE deleted = 0
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
AND pre_owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND post_owner_user_id IS NULL
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectPoolCustomerTakeCountByDate"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByDateRespVO">
|
||||
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS time,
|
||||
COUNT(*) AS customer_take_count
|
||||
FROM crm_owner_record
|
||||
WHERE deleted = 0
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
AND pre_owner_user_id IS NULL
|
||||
AND post_owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY time
|
||||
</select>
|
||||
|
||||
<select id="selectPoolCustomerPutCountByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO">
|
||||
SELECT pre_owner_user_id AS owner_user_id,
|
||||
COUNT(*) AS customer_put_count
|
||||
FROM crm_owner_record
|
||||
WHERE deleted = 0
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
AND pre_owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND post_owner_user_id IS NULL
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY pre_owner_user_id
|
||||
</select>
|
||||
|
||||
<select id="selectPoolCustomerTakeCountByUser"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsPoolSummaryByUserRespVO">
|
||||
SELECT post_owner_user_id AS owner_user_id,
|
||||
COUNT(*) AS customer_take_count
|
||||
FROM crm_owner_record
|
||||
WHERE deleted = 0
|
||||
AND biz_type = ${@cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum@CRM_CUSTOMER.type}
|
||||
AND pre_owner_user_id IS NULL
|
||||
AND post_owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
GROUP BY post_owner_user_id
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
<?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="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPerformanceTargetMapper">
|
||||
|
||||
<resultMap id="CrmStatisticsPerformanceTargetActualResultMap"
|
||||
type="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.performance.CrmStatisticsPerformanceTargetActualRespVO">
|
||||
<result property="month" column="month"/>
|
||||
<result property="currentPrice" column="current_price"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectContractActualByMonth"
|
||||
resultMap="CrmStatisticsPerformanceTargetActualResultMap">
|
||||
SELECT MONTH(order_date) AS month,
|
||||
IFNULL(SUM(total_price), 0) AS current_price
|
||||
FROM crm_contract
|
||||
WHERE deleted = 0
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND YEAR(order_date) = #{year}
|
||||
GROUP BY MONTH(order_date)
|
||||
</select>
|
||||
|
||||
<select id="selectReceivableActualByMonth"
|
||||
resultMap="CrmStatisticsPerformanceTargetActualResultMap">
|
||||
SELECT MONTH(return_time) AS month,
|
||||
IFNULL(SUM(price), 0) AS current_price
|
||||
FROM crm_receivable
|
||||
WHERE deleted = 0
|
||||
AND audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND YEAR(return_time) = #{year}
|
||||
GROUP BY MONTH(return_time)
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -1,68 +0,0 @@
|
||||
<?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="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsProductMapper">
|
||||
|
||||
<sql id="ProductStatisticsWhere">
|
||||
contract.deleted = 0
|
||||
AND contract_product.deleted = 0
|
||||
AND product.deleted = 0
|
||||
AND contract.audit_status = ${@cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum@APPROVE.status}
|
||||
AND contract.owner_user_id IN
|
||||
<foreach collection="userIds" item="userId" open="(" close=")" separator=",">
|
||||
#{userId}
|
||||
</foreach>
|
||||
AND contract.order_date BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime}
|
||||
<if test="categoryIds != null and categoryIds.size() > 0">
|
||||
AND product.category_id IN
|
||||
<foreach collection="categoryIds" item="categoryId" open="(" close=")" separator=",">
|
||||
#{categoryId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="productId != null">
|
||||
AND product.id = #{productId}
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<select id="selectProductSalesList"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductSalesRespVO">
|
||||
SELECT category.id AS category_id,
|
||||
IFNULL(category.name, '未分类') AS category_name,
|
||||
product.id AS product_id,
|
||||
product.name AS product_name,
|
||||
contract.id AS contract_id,
|
||||
contract.no AS contract_no,
|
||||
contract.name AS contract_name,
|
||||
contract.owner_user_id,
|
||||
owner.nickname AS owner_user_name,
|
||||
customer.id AS customer_id,
|
||||
customer.name AS customer_name,
|
||||
contract_product.product_price,
|
||||
contract_product.count AS product_count,
|
||||
contract_product.total_price AS product_total_price
|
||||
FROM crm_contract_product AS contract_product
|
||||
INNER JOIN crm_contract AS contract ON contract_product.contract_id = contract.id
|
||||
INNER JOIN crm_product AS product ON contract_product.product_id = product.id
|
||||
LEFT JOIN crm_product_category AS category ON product.category_id = category.id AND category.deleted = 0
|
||||
LEFT JOIN crm_customer AS customer ON contract.customer_id = customer.id AND customer.deleted = 0
|
||||
LEFT JOIN system_users AS owner ON contract.owner_user_id = owner.id AND owner.deleted = 0
|
||||
WHERE <include refid="ProductStatisticsWhere"/>
|
||||
ORDER BY category.id ASC, product.id ASC, contract.order_date DESC
|
||||
</select>
|
||||
|
||||
<select id="selectProductCategorySummary"
|
||||
resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.product.CrmStatisticsProductCategoryRespVO">
|
||||
SELECT category.id AS category_id,
|
||||
IFNULL(category.name, '未分类') AS category_name,
|
||||
COUNT(DISTINCT contract.id) AS contract_count,
|
||||
IFNULL(SUM(contract_product.count), 0) AS product_count,
|
||||
IFNULL(SUM(contract_product.total_price), 0) AS product_total_price
|
||||
FROM crm_contract_product AS contract_product
|
||||
INNER JOIN crm_contract AS contract ON contract_product.contract_id = contract.id
|
||||
INNER JOIN crm_product AS product ON contract_product.product_id = product.id
|
||||
LEFT JOIN crm_product_category AS category ON product.category_id = category.id AND category.deleted = 0
|
||||
WHERE <include refid="ProductStatisticsWhere"/>
|
||||
GROUP BY category.id, category.name
|
||||
ORDER BY product_count DESC, product_total_price DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
-422
@@ -1,422 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.codegen.inner;
|
||||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link CodegenEngine} 的 Vue3 + Vben5 + Antdv Next 单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class CodegenEngineVben5AntdvNextTest extends CodegenEngineAbstractTest {
|
||||
|
||||
private static final Pattern ANTDV_NEXT_VALUE_IMPORT_PATTERN = Pattern.compile(
|
||||
"import\\s*\\{([^}]*)}\\s*from\\s*'antdv-next';?", Pattern.DOTALL);
|
||||
private static final Pattern RANGE_PICKER_COMPONENT_PATTERN = Pattern.compile("<RangePicker\\b");
|
||||
private static final Pattern RANGE_PICKER_HELPER_BINDING_PATTERN = Pattern.compile(
|
||||
"\\bv-bind\\s*=\\s*([\"'])\\s*getRangePickerDefaultProps\\s*\\(\\s*\\)\\s*\\1");
|
||||
private static final Pattern RANGE_PICKER_HELPER_CALL_PATTERN = Pattern.compile(
|
||||
"\\bgetRangePickerDefaultProps\\s*\\(\\s*\\)");
|
||||
private static final Pattern RANGE_PICKER_HELPER_IMPORT_PATTERN = Pattern.compile(
|
||||
"import\\s*\\{[^}]*\\bgetRangePickerDefaultProps\\b[^}]*}"
|
||||
+ "\\s*from\\s*'#/utils(?:/rangePickerProps)?';?", Pattern.DOTALL);
|
||||
private static final Pattern SCHEMA_RANGE_PICKER_COMPONENT_PATTERN = Pattern.compile(
|
||||
"\\bcomponent\\s*:\\s*([\"'])RangePicker\\1");
|
||||
private static final Pattern SCHEMA_RANGE_PICKER_HELPER_SPREAD_PATTERN = Pattern.compile(
|
||||
"\\.\\.\\.\\s*getRangePickerDefaultProps\\s*\\(\\s*\\)");
|
||||
|
||||
private static final String IMPORT_FORM_PATH =
|
||||
"yudao-ui-admin-vben/src/views/infra/demo/modules/import-form.vue";
|
||||
private static final String SCHEMA_DATA_PATH =
|
||||
"yudao-ui-admin-vben/src/views/infra/demo/data.ts";
|
||||
private static final String STUDENT_CONTACT_FORM_PATH =
|
||||
"yudao-ui-admin-vben/src/views/infra/demo/modules/student-contact-form.vue";
|
||||
private static final String STUDENT_TEACHER_FORM_PATH =
|
||||
"yudao-ui-admin-vben/src/views/infra/demo/modules/student-teacher-form.vue";
|
||||
|
||||
private static final List<String> INNER_TEMPLATE_PATHS = Arrays.asList(
|
||||
"codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_inner.vue.vm",
|
||||
"codegen/vue3_vben5_antdv_next/general/views/modules/list_sub_inner.vue.vm",
|
||||
"codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_inner.vue.vm",
|
||||
"codegen/vue3_vben5_antdv_next/schema/views/modules/list_sub_inner.vue.vm");
|
||||
|
||||
@Test
|
||||
public void testExecute_schemaMasterNormal() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_SCHEMA.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_NORMAL);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertSchemaRangePickerGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_schemaMasterErp() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_SCHEMA.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_ERP);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertSchemaRangePickerGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_schemaMasterInner() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_SCHEMA.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_INNER);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertSchemaRangePickerGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalTree() {
|
||||
// 调用
|
||||
Map<String, String> result = executeTree(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType());
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertRuleGenerated(result);
|
||||
assertComponentGenerated(result, "FormItem");
|
||||
assertComponentGenerated(result, "TreeSelect");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalMasterNormal() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_NORMAL);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertRuleGenerated(result);
|
||||
assertGeneralCrudComponentsGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalMasterNormal_oneToOneJoinColumnOnlySelect() {
|
||||
// 调用
|
||||
Map<String, String> result = executeGeneralMasterNormalWithOneToOneJoinSelect();
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
String content = result.get(STUDENT_TEACHER_FORM_PATH);
|
||||
assertNotNull(content, STUDENT_TEACHER_FORM_PATH + ":未生成一对一子表表单");
|
||||
assertTrue(content.contains("<Select v-model:value=\"formData.studentId\""),
|
||||
STUDENT_TEACHER_FORM_PATH + ":应渲染 join 列的 Select 组件");
|
||||
assertTrue(content.contains("<SelectOption"), STUDENT_TEACHER_FORM_PATH + ":应渲染 SelectOption 组件");
|
||||
assertRootValueImport(STUDENT_TEACHER_FORM_PATH, content, "\\bSelectOption\\b");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalMasterNormal_oneToManyJoinColumnOnlySelect() {
|
||||
// 调用
|
||||
Map<String, String> result = executeGeneralMasterNormalWithOneToManyJoinSelect();
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
String content = result.get(STUDENT_CONTACT_FORM_PATH);
|
||||
assertNotNull(content, STUDENT_CONTACT_FORM_PATH + ":未生成一对多子表表单");
|
||||
assertTrue(content.contains("<VxeTable"), STUDENT_CONTACT_FORM_PATH + ":应生成一对多子表表格");
|
||||
assertFalse(content.contains("<VxeColumn field=\"studentId\""),
|
||||
STUDENT_CONTACT_FORM_PATH + ":不应生成 join 列");
|
||||
assertFalse(content.contains("<Select v-model:value=\"row.studentId\""),
|
||||
STUDENT_CONTACT_FORM_PATH + ":不应渲染 join 列的 Select 组件");
|
||||
assertFalse(content.contains("<SelectOption"), STUDENT_CONTACT_FORM_PATH + ":不应渲染 SelectOption 组件");
|
||||
assertFalse(createRootImportPattern("import\\s*\\{", "\\bSelectOption\\b").matcher(content).find(),
|
||||
STUDENT_CONTACT_FORM_PATH + ":不应导入 SelectOption 组件");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalMasterInner() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_INNER);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertRuleGenerated(result);
|
||||
assertGeneralCrudComponentsGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_generalMasterErp() {
|
||||
// 调用
|
||||
Map<String, String> result = executeMaster(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType(),
|
||||
CodegenTemplateTypeEnum.MASTER_ERP);
|
||||
|
||||
// 断言
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
assertRuleGenerated(result);
|
||||
assertGeneralCrudComponentsGenerated(result);
|
||||
assertTabsGenerated(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_oneImportEnable() {
|
||||
// 开启 import 开关
|
||||
codegenProperties.setImportEnable(true);
|
||||
codegenEngine.initGlobalBindingMap();
|
||||
|
||||
// 调用并断言
|
||||
assertImportFormCompatibleWithAntdvNext(
|
||||
executeOne(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_SCHEMA.getType()));
|
||||
assertImportFormCompatibleWithAntdvNext(
|
||||
executeOne(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerTemplatesParseAntdvNext() {
|
||||
INNER_TEMPLATE_PATHS.forEach(path -> {
|
||||
String template = ResourceUtil.readUtf8Str(path);
|
||||
assertFalse(template.contains("codegen/vue3_vben5_antd/"), path + ":不应引用 Ant Design Vue 模板");
|
||||
assertTrue(template.contains("codegen/vue3_vben5_antdv_next/"), path + ":应引用 Antdv Next 模板");
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, String> executeOne(Integer frontType) {
|
||||
CodegenTableDO table = getTable("student")
|
||||
.setFrontType(frontType)
|
||||
.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
|
||||
return codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"), null, null);
|
||||
}
|
||||
|
||||
private Map<String, String> executeTree(Integer frontType) {
|
||||
CodegenTableDO table = getTable("category")
|
||||
.setFrontType(frontType)
|
||||
.setTemplateType(CodegenTemplateTypeEnum.TREE.getType());
|
||||
return codegenEngine.execute(DbType.MYSQL, table, getColumnList("category"), null, null);
|
||||
}
|
||||
|
||||
private Map<String, String> executeGeneralMasterNormalWithOneToOneJoinSelect() {
|
||||
return executeGeneralMasterNormalWithJoinSelect("teacher", 200L, false);
|
||||
}
|
||||
|
||||
private Map<String, String> executeGeneralMasterNormalWithOneToManyJoinSelect() {
|
||||
return executeGeneralMasterNormalWithJoinSelect("contact", 100L, true);
|
||||
}
|
||||
|
||||
private Map<String, String> executeGeneralMasterNormalWithJoinSelect(String subTableName,
|
||||
Long subJoinColumnId,
|
||||
boolean subJoinMany) {
|
||||
CodegenTableDO table = getTable("student")
|
||||
.setFrontType(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType())
|
||||
.setTemplateType(CodegenTemplateTypeEnum.MASTER_NORMAL.getType());
|
||||
CodegenTableDO subTable = getTable(subTableName)
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
|
||||
.setFrontType(CodegenFrontTypeEnum.VUE3_VBEN5_ANTDV_NEXT_GENERAL.getType())
|
||||
.setSubJoinColumnId(subJoinColumnId).setSubJoinMany(subJoinMany);
|
||||
List<CodegenColumnDO> subColumns = getColumnList(subTableName);
|
||||
// 稀疏场景:join 列是唯一的 select,避免其它 select 掩盖 join 列对导入判定的影响。
|
||||
subColumns.forEach(column -> {
|
||||
if (subJoinColumnId.equals(column.getId())) {
|
||||
column.setHtmlType("select");
|
||||
column.setDictType("");
|
||||
} else if ("select".equals(column.getHtmlType())) {
|
||||
column.setHtmlType("input");
|
||||
}
|
||||
});
|
||||
return codegenEngine.execute(DbType.MYSQL, table, getColumnList("student"),
|
||||
List.of(subTable), List.of(subColumns));
|
||||
}
|
||||
|
||||
private Map<String, String> executeMaster(Integer frontType, CodegenTemplateTypeEnum templateType) {
|
||||
// 准备参数
|
||||
CodegenTableDO table = getTable("student")
|
||||
.setFrontType(frontType)
|
||||
.setTemplateType(templateType.getType());
|
||||
List<CodegenColumnDO> columns = getColumnList("student");
|
||||
// 准备参数(子表)
|
||||
CodegenTableDO contactTable = getTable("contact")
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
|
||||
.setFrontType(frontType)
|
||||
.setSubJoinColumnId(100L).setSubJoinMany(true);
|
||||
List<CodegenColumnDO> contactColumns = getColumnList("contact");
|
||||
// 准备参数(班主任)
|
||||
CodegenTableDO teacherTable = getTable("teacher")
|
||||
.setTemplateType(CodegenTemplateTypeEnum.SUB.getType())
|
||||
.setFrontType(frontType)
|
||||
.setSubJoinColumnId(200L).setSubJoinMany(false);
|
||||
List<CodegenColumnDO> teacherColumns = getColumnList("teacher");
|
||||
|
||||
// 调用
|
||||
return codegenEngine.execute(DbType.MYSQL, table, columns,
|
||||
Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns));
|
||||
}
|
||||
|
||||
private static void assertGeneratedCodeCompatibleWithAntdvNext(Map<String, String> result) {
|
||||
assertTrue(result.values().stream().anyMatch(content -> content.contains("from 'antdv-next")),
|
||||
"生成结果应使用 antdv-next");
|
||||
result.forEach((filePath, content) -> {
|
||||
assertFalse(content.contains("ant-design-vue"), filePath + ":不应使用 ant-design-vue");
|
||||
assertFalse(content.contains("antdv-next/es/"), filePath + ":不应使用未导出的 antdv-next 深层路径");
|
||||
assertFalse(content.contains("<Form.Item"), filePath + ":不应使用 Form.Item");
|
||||
assertFalse(content.contains("</Form.Item>"), filePath + ":不应使用 Form.Item");
|
||||
assertRootValueImportsSorted(filePath, content);
|
||||
assertRootValueImportMatchesUsage(filePath, content,
|
||||
"\\bDateRangePicker\\s+as\\s+RangePicker\\b", "<RangePicker\\b", "RangePicker");
|
||||
assertRootValueImportMatchesUsage(filePath, content,
|
||||
"\\bFormItem\\b", "<FormItem\\b", "FormItem");
|
||||
assertRootValueImportMatchesUsage(filePath, content,
|
||||
"\\bSelectOption\\b", "<SelectOption\\b", "SelectOption");
|
||||
assertRootValueImportMatchesUsage(filePath, content,
|
||||
"\\bTabPane\\b", "<TabPane\\b", "TabPane");
|
||||
assertRootValueImportMatchesUsage(filePath, content,
|
||||
"\\bTabs\\b", "<Tabs\\b", "Tabs");
|
||||
assertRootTypeImportMatchesUsage(filePath, content,
|
||||
"\\bRule\\b", "\\bRule\\b", "Rule");
|
||||
assertRangePickerHelperImportMatchesUsage(filePath, content);
|
||||
if (filePath.endsWith(".vue")) {
|
||||
assertRangePickerHelperBindingMatchesComponent(filePath, content);
|
||||
} else if (filePath.endsWith(".ts")) {
|
||||
assertSchemaRangePickerHelperMatchesComponent(filePath, content);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void assertImportFormCompatibleWithAntdvNext(Map<String, String> result) {
|
||||
assertGeneratedCodeCompatibleWithAntdvNext(result);
|
||||
String content = result.get(IMPORT_FORM_PATH);
|
||||
assertNotNull(content, IMPORT_FORM_PATH + ":未生成导入表单");
|
||||
assertTrue(content.contains("<Upload"), IMPORT_FORM_PATH + ":应生成 Upload 组件");
|
||||
assertRootValueImport(IMPORT_FORM_PATH, content, "\\bUpload\\b");
|
||||
assertTrue(content.contains("beforeUpload(file: File)"), IMPORT_FORM_PATH + ":上传参数应使用原生 File 类型");
|
||||
assertFalse(content.contains("UploadFile"), IMPORT_FORM_PATH + ":不应使用上传列表项类型 UploadFile");
|
||||
assertFalse(content.contains("as unknown as File"), IMPORT_FORM_PATH + ":不应通过双重断言转换上传文件");
|
||||
assertFalse(content.contains("FileType"), IMPORT_FORM_PATH + ":不应继续使用旧 FileType");
|
||||
}
|
||||
|
||||
private static void assertTabsGenerated(Map<String, String> result) {
|
||||
assertComponentGenerated(result, "Tabs");
|
||||
assertComponentGenerated(result, "TabPane");
|
||||
}
|
||||
|
||||
private static void assertGeneralCrudComponentsGenerated(Map<String, String> result) {
|
||||
assertComponentGenerated(result, "FormItem");
|
||||
assertComponentGenerated(result, "RangePicker");
|
||||
assertComponentGenerated(result, "SelectOption");
|
||||
}
|
||||
|
||||
private static void assertRuleGenerated(Map<String, String> result) {
|
||||
assertTrue(result.values().stream().map(CodegenEngineVben5AntdvNextTest::removeAntdvNextRootImports)
|
||||
.anyMatch(content -> Pattern.compile("\\bRule\\b").matcher(content).find()),
|
||||
"生成结果应包含 Rule 类型校验规则");
|
||||
}
|
||||
|
||||
private static void assertSchemaRangePickerGenerated(Map<String, String> result) {
|
||||
String content = result.get(SCHEMA_DATA_PATH);
|
||||
assertNotNull(content, SCHEMA_DATA_PATH + ":未生成 Schema 配置");
|
||||
long componentCount = countMatches(SCHEMA_RANGE_PICKER_COMPONENT_PATTERN, content);
|
||||
assertTrue(componentCount > 0, SCHEMA_DATA_PATH + ":应生成 RangePicker 组件配置");
|
||||
}
|
||||
|
||||
private static void assertComponentGenerated(Map<String, String> result, String component) {
|
||||
assertTrue(result.values().stream().anyMatch(content -> content.contains("<" + component)),
|
||||
"生成结果应包含 " + component + " 组件");
|
||||
}
|
||||
|
||||
private static void assertRootValueImportMatchesUsage(String filePath, String content,
|
||||
String expectedImportRegex, String usageRegex,
|
||||
String bindingName) {
|
||||
assertRootImportMatchesUsage(filePath, content, "import\\s*\\{", expectedImportRegex,
|
||||
usageRegex, bindingName);
|
||||
}
|
||||
|
||||
private static void assertRootTypeImportMatchesUsage(String filePath, String content,
|
||||
String expectedImportRegex, String usageRegex,
|
||||
String bindingName) {
|
||||
assertRootImportMatchesUsage(filePath, content, "import\\s+type\\s*\\{", expectedImportRegex,
|
||||
usageRegex, bindingName);
|
||||
}
|
||||
|
||||
private static void assertRootImportMatchesUsage(String filePath, String content, String importPrefix,
|
||||
String expectedImportRegex, String usageRegex,
|
||||
String bindingName) {
|
||||
boolean imported = createRootImportPattern(importPrefix, expectedImportRegex).matcher(content).find();
|
||||
boolean used = Pattern.compile(usageRegex).matcher(removeAntdvNextRootImports(content)).find();
|
||||
assertEquals(used, imported, filePath + ":" + bindingName + (used ? " 缺少合法根导入" : " 存在孤立根导入"));
|
||||
}
|
||||
|
||||
private static String removeAntdvNextRootImports(String content) {
|
||||
Pattern pattern = Pattern.compile("import\\s+(?:type\\s+)?\\{[^}]*}\\s*from\\s*'antdv-next';?",
|
||||
Pattern.DOTALL);
|
||||
return pattern.matcher(content).replaceAll("");
|
||||
}
|
||||
|
||||
private static void assertRootValueImport(String filePath, String content, String expectedRegex) {
|
||||
assertRootImport(filePath, content, "import\\s*\\{", expectedRegex);
|
||||
}
|
||||
|
||||
private static void assertRootValueImportsSorted(String filePath, String content) {
|
||||
var matcher = ANTDV_NEXT_VALUE_IMPORT_PATTERN.matcher(content);
|
||||
while (matcher.find()) {
|
||||
List<String> actual = Arrays.stream(matcher.group(1).split(","))
|
||||
.map(String::trim)
|
||||
.filter(item -> !item.isEmpty())
|
||||
.map(item -> item.replaceFirst("^.*\\s+as\\s+", ""))
|
||||
.toList();
|
||||
List<String> expected = actual.stream().sorted(String.CASE_INSENSITIVE_ORDER).toList();
|
||||
assertEquals(expected, actual, filePath + ":antdv-next 命名导入未按 natural 顺序排列");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertRangePickerHelperImportMatchesUsage(String filePath, String content) {
|
||||
boolean imported = RANGE_PICKER_HELPER_IMPORT_PATTERN.matcher(content).find();
|
||||
boolean called = RANGE_PICKER_HELPER_CALL_PATTERN.matcher(content).find();
|
||||
assertEquals(called, imported,
|
||||
filePath + ":getRangePickerDefaultProps() 调用与导入不一致");
|
||||
}
|
||||
|
||||
private static void assertRangePickerHelperBindingMatchesComponent(String filePath, String content) {
|
||||
long componentCount = countMatches(RANGE_PICKER_COMPONENT_PATTERN, content);
|
||||
long bindingCount = countMatches(RANGE_PICKER_HELPER_BINDING_PATTERN, content);
|
||||
assertEquals(componentCount, bindingCount,
|
||||
filePath + ":RangePicker 与 getRangePickerDefaultProps() 绑定数量不一致");
|
||||
}
|
||||
|
||||
private static void assertSchemaRangePickerHelperMatchesComponent(String filePath, String content) {
|
||||
long componentCount = countMatches(SCHEMA_RANGE_PICKER_COMPONENT_PATTERN, content);
|
||||
long helperSpreadCount = countMatches(SCHEMA_RANGE_PICKER_HELPER_SPREAD_PATTERN, content);
|
||||
assertEquals(componentCount, helperSpreadCount,
|
||||
filePath + ":Schema RangePicker 与 getRangePickerDefaultProps() 展开数量不一致");
|
||||
}
|
||||
|
||||
private static long countMatches(Pattern pattern, String content) {
|
||||
return pattern.matcher(content).results().count();
|
||||
}
|
||||
|
||||
private static void assertRootImport(String filePath, String content, String importPrefix, String expectedRegex) {
|
||||
Pattern pattern = createRootImportPattern(importPrefix, expectedRegex);
|
||||
assertTrue(pattern.matcher(content).find(), filePath + ":缺少合法的 antdv-next 根导入 " + expectedRegex);
|
||||
}
|
||||
|
||||
private static Pattern createRootImportPattern(String importPrefix, String expectedRegex) {
|
||||
return Pattern.compile(importPrefix + "[^}]*" + expectedRegex
|
||||
+ "[^}]*}\\s*from\\s*'antdv-next';?", Pattern.DOTALL);
|
||||
}
|
||||
|
||||
}
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
[ {
|
||||
"contentPath" : "java/InfraStudentPageReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentRespVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentSaveReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentController",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
|
||||
}, {
|
||||
"contentPath" : "xml/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImpl",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentService",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImplTest",
|
||||
"filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
|
||||
}, {
|
||||
"contentPath" : "java/ErrorCodeConstants_手动操作",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
|
||||
}, {
|
||||
"contentPath" : "sql/sql",
|
||||
"filePath" : "sql/sql.sql"
|
||||
}, {
|
||||
"contentPath" : "sql/h2",
|
||||
"filePath" : "sql/h2.sql"
|
||||
}, {
|
||||
"contentPath" : "ts/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"
|
||||
}, {
|
||||
"contentPath" : "vue/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/search-form",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/form/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/detail/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/student-contact/form/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/student-contact/form/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/student-teacher/form/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/student-teacher/form/index.vue"
|
||||
} ]
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
|
||||
// ========== 学生 TODO 补充编号 ==========
|
||||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
|
||||
ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在");
|
||||
ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在");
|
||||
ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在");
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生联系人 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student_contact")
|
||||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentContactDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 学生联系人 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
|
||||
|
||||
default PageResult<InfraStudentContactDO> selectPage(PageParam reqVO, Long studentId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentContactDO>()
|
||||
.eq(InfraStudentContactDO::getStudentId, studentId)
|
||||
.orderByDesc(InfraStudentContactDO::getId));
|
||||
}
|
||||
|
||||
default int deleteByStudentId(Long studentId) {
|
||||
return delete(InfraStudentContactDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentIds(List<Long> studentIds) {
|
||||
return deleteBatch(InfraStudentContactDO::getStudentId, studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.validation.*;
|
||||
import jakarta.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
|
||||
|
||||
@Tag(name = "管理后台 - 学生")
|
||||
@RestController
|
||||
@RequestMapping("/infra/student")
|
||||
@Validated
|
||||
public class InfraStudentController {
|
||||
|
||||
@Resource
|
||||
private InfraStudentService studentService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:create')")
|
||||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
|
||||
return success(studentService.createStudent(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:update')")
|
||||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
|
||||
studentService.updateStudent(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除学生")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
|
||||
studentService.deleteStudent(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) {
|
||||
studentService.deleteStudentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得学生")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
|
||||
InfraStudentDO student = studentService.getStudent(id);
|
||||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得学生分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
|
||||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出学生 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
|
||||
BeanUtils.toBean(list, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
@GetMapping("/student-contact/page")
|
||||
@Operation(summary = "获得学生联系人分页")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<PageResult<InfraStudentContactDO>> getStudentContactPage(PageParam pageReqVO,
|
||||
@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentContactPage(pageReqVO, studentId));
|
||||
}
|
||||
|
||||
@PostMapping("/student-contact/create")
|
||||
@Operation(summary = "创建学生联系人")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:create')")
|
||||
public CommonResult<Long> createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
|
||||
return success(studentService.createStudentContact(studentContact));
|
||||
}
|
||||
|
||||
@PutMapping("/student-contact/update")
|
||||
@Operation(summary = "更新学生联系人")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:update')")
|
||||
public CommonResult<Boolean> updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) {
|
||||
studentService.updateStudentContact(studentContact);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/student-contact/delete")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@Operation(summary = "删除学生联系人")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentContact(@RequestParam("id") Long id) {
|
||||
studentService.deleteStudentContact(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/student-contact/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除学生联系人")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentContactList(@RequestParam("ids") List<Long> ids) {
|
||||
studentService.deleteStudentContactListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/student-contact/get")
|
||||
@Operation(summary = "获得学生联系人")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentContactDO> getStudentContact(@RequestParam("id") Long id) {
|
||||
return success(studentService.getStudentContact(id));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
@GetMapping("/student-teacher/page")
|
||||
@Operation(summary = "获得学生班主任分页")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<PageResult<InfraStudentTeacherDO>> getStudentTeacherPage(PageParam pageReqVO,
|
||||
@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentTeacherPage(pageReqVO, studentId));
|
||||
}
|
||||
|
||||
@PostMapping("/student-teacher/create")
|
||||
@Operation(summary = "创建学生班主任")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:create')")
|
||||
public CommonResult<Long> createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
|
||||
return success(studentService.createStudentTeacher(studentTeacher));
|
||||
}
|
||||
|
||||
@PutMapping("/student-teacher/update")
|
||||
@Operation(summary = "更新学生班主任")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:update')")
|
||||
public CommonResult<Boolean> updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) {
|
||||
studentService.updateStudentTeacher(studentTeacher);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/student-teacher/delete")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@Operation(summary = "删除学生班主任")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentTeacher(@RequestParam("id") Long id) {
|
||||
studentService.deleteStudentTeacher(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/student-teacher/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除学生班主任")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentTeacherList(@RequestParam("ids") List<Long> ids) {
|
||||
studentService.deleteStudentTeacherListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/student-teacher/get")
|
||||
@Operation(summary = "获得学生班主任")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentTeacherDO> getStudentTeacher(@RequestParam("id") Long id) {
|
||||
return success(studentService.getStudentTeacher(id));
|
||||
}
|
||||
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student")
|
||||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
|
||||
/**
|
||||
* 学生 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
|
||||
|
||||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
|
||||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
|
||||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
|
||||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
|
||||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
|
||||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(InfraStudentDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 学生分页 Request VO")
|
||||
@Data
|
||||
public class InfraStudentPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "名字", example = "芋头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", example = "true")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.idev.excel.annotation.*;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
|
||||
@Schema(description = "管理后台 - 学生 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class InfraStudentRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@ExcelProperty("名字")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@ExcelProperty("简介")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty(value = "性别", converter = DictConvert.class)
|
||||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
|
||||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@ExcelProperty("头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@ExcelProperty("附件")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@ExcelProperty("备注")
|
||||
private String memo;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
|
||||
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
|
||||
@Data
|
||||
public class InfraStudentSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@NotEmpty(message = "名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@NotEmpty(message = "简介不能为空")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "出生日期不能为空")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "性别不能为空")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否有效不能为空")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@NotEmpty(message = "头像不能为空")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@NotEmpty(message = "附件不能为空")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@NotEmpty(message = "备注不能为空")
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import java.util.*;
|
||||
import jakarta.validation.*;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 学生 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface InfraStudentService {
|
||||
|
||||
/**
|
||||
* 创建学生
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新学生
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除学生
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteStudent(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除学生
|
||||
*
|
||||
* @param ids 编号
|
||||
*/
|
||||
void deleteStudentListByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得学生
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 学生
|
||||
*/
|
||||
InfraStudentDO getStudent(Long id);
|
||||
|
||||
/**
|
||||
* 获得学生分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 学生分页
|
||||
*/
|
||||
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
/**
|
||||
* 获得学生联系人分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param studentId 学生编号
|
||||
* @return 学生联系人分页
|
||||
*/
|
||||
PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId);
|
||||
|
||||
/**
|
||||
* 创建学生联系人
|
||||
*
|
||||
* @param studentContact 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createStudentContact(@Valid InfraStudentContactDO studentContact);
|
||||
|
||||
/**
|
||||
* 更新学生联系人
|
||||
*
|
||||
* @param studentContact 更新信息
|
||||
*/
|
||||
void updateStudentContact(@Valid InfraStudentContactDO studentContact);
|
||||
|
||||
/**
|
||||
* 删除学生联系人
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteStudentContact(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除学生联系人
|
||||
*
|
||||
* @param ids 编号
|
||||
*/
|
||||
void deleteStudentContactListByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得学生联系人
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 学生联系人
|
||||
*/
|
||||
InfraStudentContactDO getStudentContact(Long id);
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
/**
|
||||
* 获得学生班主任分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @param studentId 学生编号
|
||||
* @return 学生班主任分页
|
||||
*/
|
||||
PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId);
|
||||
|
||||
/**
|
||||
* 创建学生班主任
|
||||
*
|
||||
* @param studentTeacher 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
|
||||
|
||||
/**
|
||||
* 更新学生班主任
|
||||
*
|
||||
* @param studentTeacher 更新信息
|
||||
*/
|
||||
void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher);
|
||||
|
||||
/**
|
||||
* 删除学生班主任
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteStudentTeacher(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除学生班主任
|
||||
*
|
||||
* @param ids 编号
|
||||
*/
|
||||
void deleteStudentTeacherListByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得学生班主任
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 学生班主任
|
||||
*/
|
||||
InfraStudentTeacherDO getStudentTeacher(Long id);
|
||||
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 学生 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class InfraStudentServiceImpl implements InfraStudentService {
|
||||
|
||||
@Resource
|
||||
private InfraStudentMapper studentMapper;
|
||||
@Resource
|
||||
private InfraStudentContactMapper studentContactMapper;
|
||||
@Resource
|
||||
private InfraStudentTeacherMapper studentTeacherMapper;
|
||||
|
||||
@Override
|
||||
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
|
||||
studentMapper.insert(student);
|
||||
|
||||
// 返回
|
||||
return student.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateStudentExists(updateReqVO.getId());
|
||||
// 更新
|
||||
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
|
||||
studentMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteStudent(Long id) {
|
||||
// 校验存在
|
||||
validateStudentExists(id);
|
||||
// 删除
|
||||
studentMapper.deleteById(id);
|
||||
|
||||
// 删除子表
|
||||
deleteStudentContactByStudentId(id);
|
||||
deleteStudentTeacherByStudentId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteStudentListByIds(List<Long> ids) {
|
||||
// 删除
|
||||
studentMapper.deleteByIds(ids);
|
||||
|
||||
// 删除子表
|
||||
deleteStudentContactByStudentIds(ids);
|
||||
deleteStudentTeacherByStudentIds(ids);
|
||||
}
|
||||
|
||||
|
||||
private void validateStudentExists(Long id) {
|
||||
if (studentMapper.selectById(id) == null) {
|
||||
throw exception(STUDENT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfraStudentDO getStudent(Long id) {
|
||||
return studentMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
|
||||
return studentMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
@Override
|
||||
public PageResult<InfraStudentContactDO> getStudentContactPage(PageParam pageReqVO, Long studentId) {
|
||||
return studentContactMapper.selectPage(pageReqVO, studentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createStudentContact(InfraStudentContactDO studentContact) {
|
||||
studentContact.clean(); // 清理掉创建、更新时间等相关属性值
|
||||
studentContactMapper.insert(studentContact);
|
||||
return studentContact.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStudentContact(InfraStudentContactDO studentContact) {
|
||||
// 校验存在
|
||||
validateStudentContactExists(studentContact.getId());
|
||||
// 更新
|
||||
studentContact.clean(); // 解决更新情况下:updateTime 不更新
|
||||
studentContactMapper.updateById(studentContact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStudentContact(Long id) {
|
||||
// 删除
|
||||
studentContactMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStudentContactListByIds(List<Long> ids) {
|
||||
// 删除
|
||||
studentContactMapper.deleteByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfraStudentContactDO getStudentContact(Long id) {
|
||||
return studentContactMapper.selectById(id);
|
||||
}
|
||||
|
||||
private void validateStudentContactExists(Long id) {
|
||||
if (studentContactMapper.selectById(id) == null) {
|
||||
throw exception(STUDENT_CONTACT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteStudentContactByStudentId(Long studentId) {
|
||||
studentContactMapper.deleteByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void deleteStudentContactByStudentIds(List<Long> studentIds) {
|
||||
studentContactMapper.deleteByStudentIds(studentIds);
|
||||
}
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
@Override
|
||||
public PageResult<InfraStudentTeacherDO> getStudentTeacherPage(PageParam pageReqVO, Long studentId) {
|
||||
return studentTeacherMapper.selectPage(pageReqVO, studentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) {
|
||||
// 校验是否已经存在
|
||||
if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) {
|
||||
throw exception(STUDENT_TEACHER_EXISTS);
|
||||
}
|
||||
// 插入
|
||||
studentTeacher.clean(); // 清理掉创建、更新时间等相关属性值
|
||||
studentTeacherMapper.insert(studentTeacher);
|
||||
return studentTeacher.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) {
|
||||
// 校验存在
|
||||
validateStudentTeacherExists(studentTeacher.getId());
|
||||
// 更新
|
||||
studentTeacher.clean(); // 解决更新情况下:updateTime 不更新
|
||||
studentTeacherMapper.updateById(studentTeacher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStudentTeacher(Long id) {
|
||||
// 删除
|
||||
studentTeacherMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteStudentTeacherListByIds(List<Long> ids) {
|
||||
// 删除
|
||||
studentTeacherMapper.deleteByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfraStudentTeacherDO getStudentTeacher(Long id) {
|
||||
return studentTeacherMapper.selectById(id);
|
||||
}
|
||||
|
||||
private void validateStudentTeacherExists(Long id) {
|
||||
if (studentTeacherMapper.selectById(id) == null) {
|
||||
throw exception(STUDENT_TEACHER_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteStudentTeacherByStudentId(Long studentId) {
|
||||
studentTeacherMapper.deleteByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void deleteStudentTeacherByStudentIds(List<Long> studentIds) {
|
||||
studentTeacherMapper.deleteByStudentIds(studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link InfraStudentServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(InfraStudentServiceImpl.class)
|
||||
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private InfraStudentServiceImpl studentService;
|
||||
|
||||
@Resource
|
||||
private InfraStudentMapper studentMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateStudent_success() {
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
|
||||
|
||||
// 调用
|
||||
Long studentId = studentService.createStudent(createReqVO);
|
||||
// 断言
|
||||
assertNotNull(studentId);
|
||||
// 校验记录的属性是否正确
|
||||
InfraStudentDO student = studentMapper.selectById(studentId);
|
||||
assertPojoEquals(createReqVO, student, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateStudent_success() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
|
||||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
|
||||
o.setId(dbStudent.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
studentService.updateStudent(updateReqVO);
|
||||
// 校验是否更新正确
|
||||
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(updateReqVO, student);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateStudent_notExists() {
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteStudent_success() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
|
||||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbStudent.getId();
|
||||
|
||||
// 调用
|
||||
studentService.deleteStudent(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(studentMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteStudent_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetStudentPage() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
|
||||
o.setName(null);
|
||||
o.setBirthday(null);
|
||||
o.setSex(null);
|
||||
o.setEnabled(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
studentMapper.insert(dbStudent);
|
||||
// 测试 name 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
|
||||
// 测试 birthday 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
|
||||
// 测试 sex 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
|
||||
// 测试 enabled 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
|
||||
// 测试 createTime 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
|
||||
reqVO.setName(null);
|
||||
reqVO.setBirthday(null);
|
||||
reqVO.setSex(null);
|
||||
reqVO.setEnabled(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbStudent, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生班主任 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student_teacher")
|
||||
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentTeacherDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 学生班主任 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
|
||||
|
||||
default PageResult<InfraStudentTeacherDO> selectPage(PageParam reqVO, Long studentId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentTeacherDO>()
|
||||
.eq(InfraStudentTeacherDO::getStudentId, studentId)
|
||||
.orderByDesc(InfraStudentTeacherDO::getId));
|
||||
}
|
||||
default InfraStudentTeacherDO selectByStudentId(Long studentId) {
|
||||
return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentId(Long studentId) {
|
||||
return delete(InfraStudentTeacherDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentIds(List<Long> studentIds) {
|
||||
return deleteBatch(InfraStudentTeacherDO::getStudentId, studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
|
||||
CREATE TABLE IF NOT EXISTS "infra_student" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar NOT NULL,
|
||||
"birthday" varchar NOT NULL,
|
||||
"sex" int NOT NULL,
|
||||
"enabled" bit NOT NULL,
|
||||
"avatar" varchar NOT NULL,
|
||||
"video" varchar NOT NULL,
|
||||
"memo" varchar NOT NULL,
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '学生表';
|
||||
|
||||
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
|
||||
DELETE FROM "infra_student";
|
||||
@@ -1,54 +0,0 @@
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'学生管理', '', 2, 0, 888,
|
||||
'student', '', 'infra/demo/index', 0, 'InfraStudent'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生查询', 'infra:student:query', 3, 1, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生创建', 'infra:student:create', 3, 2, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生更新', 'infra:student:update', 3, 3, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生删除', 'infra:student:delete', 3, 4, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生导出', 'infra:student:export', 3, 5, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
@@ -1,125 +0,0 @@
|
||||
import type { PageParam, PageResult } from '@/http/types'
|
||||
import { http } from '@/http/http'
|
||||
|
||||
/** 学生联系人信息 */
|
||||
export interface StudentContact {
|
||||
id?: number
|
||||
studentId: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video?: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
/** 学生班主任信息 */
|
||||
export interface StudentTeacher {
|
||||
id?: number
|
||||
studentId: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video?: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
/** 学生信息 */
|
||||
export interface Student {
|
||||
id?: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
/** 获取学生分页列表 */
|
||||
export function getStudentPage(params: PageParam) {
|
||||
return http.get<PageResult<Student>>('/infra/student/page', params)
|
||||
}
|
||||
|
||||
/** 获取学生详情 */
|
||||
export function getStudent(id: number) {
|
||||
return http.get<Student>(`/infra/student/get?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建学生 */
|
||||
export function createStudent(data: Student) {
|
||||
return http.post<number>('/infra/student/create', data)
|
||||
}
|
||||
|
||||
/** 更新学生 */
|
||||
export function updateStudent(data: Student) {
|
||||
return http.put<boolean>('/infra/student/update', data)
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
export function deleteStudent(id: number) {
|
||||
return http.delete<boolean>(`/infra/student/delete?id=${id}`)
|
||||
}
|
||||
|
||||
// ==================== 子表:学生联系人 ====================
|
||||
|
||||
/** 获取学生联系人分页列表 */
|
||||
export function getStudentContactPage(params: PageParam & { studentId: number }) {
|
||||
return http.get<PageResult<StudentContact>>('/infra/student/student-contact/page', params)
|
||||
}
|
||||
|
||||
/** 获取学生联系人详情 */
|
||||
export function getStudentContact(id: number) {
|
||||
return http.get<StudentContact>(`/infra/student/student-contact/get?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建学生联系人 */
|
||||
export function createStudentContact(data: StudentContact) {
|
||||
return http.post<number>('/infra/student/student-contact/create', data)
|
||||
}
|
||||
|
||||
/** 更新学生联系人 */
|
||||
export function updateStudentContact(data: StudentContact) {
|
||||
return http.put<boolean>('/infra/student/student-contact/update', data)
|
||||
}
|
||||
|
||||
/** 删除学生联系人 */
|
||||
export function deleteStudentContact(id: number) {
|
||||
return http.delete<boolean>(`/infra/student/student-contact/delete?id=${id}`)
|
||||
}
|
||||
|
||||
// ==================== 子表:学生班主任 ====================
|
||||
|
||||
/** 获取学生班主任分页列表 */
|
||||
export function getStudentTeacherPage(params: PageParam & { studentId: number }) {
|
||||
return http.get<PageResult<StudentTeacher>>('/infra/student/student-teacher/page', params)
|
||||
}
|
||||
|
||||
/** 获取学生班主任详情 */
|
||||
export function getStudentTeacher(id: number) {
|
||||
return http.get<StudentTeacher>(`/infra/student/student-teacher/get?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建学生班主任 */
|
||||
export function createStudentTeacher(data: StudentTeacher) {
|
||||
return http.post<number>('/infra/student/student-teacher/create', data)
|
||||
}
|
||||
|
||||
/** 更新学生班主任 */
|
||||
export function updateStudentTeacher(data: StudentTeacher) {
|
||||
return http.put<boolean>('/infra/student/student-teacher/update', data)
|
||||
}
|
||||
|
||||
/** 删除学生班主任 */
|
||||
export function deleteStudentTeacher(id: number) {
|
||||
return http.delete<boolean>(`/infra/student/student-teacher/delete?id=${id}`)
|
||||
}
|
||||
-342
@@ -1,342 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar title="学生详情" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" />
|
||||
|
||||
<view class="pb-160rpx">
|
||||
<!-- 主表详情 -->
|
||||
<wd-cell-group border title="学生信息">
|
||||
<wd-cell title="编号" :value="formData?.id ?? '-'" />
|
||||
<wd-cell title="名字" :value="formData?.name ?? '-'" />
|
||||
<wd-cell title="简介" :value="formData?.description ?? '-'" />
|
||||
<wd-cell title="出生日期" :value="formatDateTime(formData?.birthday) || '-'" />
|
||||
<wd-cell title="性别">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="formData?.sex" />
|
||||
</wd-cell>
|
||||
<wd-cell title="是否有效">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="formData?.enabled" />
|
||||
</wd-cell>
|
||||
<wd-cell title="头像" :value="formData?.avatar ?? '-'" />
|
||||
<wd-cell title="附件" :value="formData?.video ?? '-'" />
|
||||
<wd-cell title="备注" :value="formData?.memo ?? '-'" />
|
||||
<wd-cell title="创建时间" :value="formatDateTime(formData?.createTime) || '-'" />
|
||||
</wd-cell-group>
|
||||
|
||||
<!-- 子表:学生联系人 -->
|
||||
<view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx">
|
||||
<text class="text-28rpx text-[#333] font-semibold">学生联系人({{ studentContactTotal }})</text>
|
||||
<wd-button
|
||||
v-if="hasAccessByCodes(['infra:student:create'])"
|
||||
size="small" type="primary" variant="plain" @click="openStudentContact()"
|
||||
>
|
||||
添加
|
||||
</wd-button>
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<z-paging
|
||||
ref="studentContactPagingRef"
|
||||
v-model="studentContactList"
|
||||
:fixed="false"
|
||||
:auto="false"
|
||||
height="560rpx"
|
||||
:default-page-size="10"
|
||||
:refresher-enabled="false"
|
||||
:inside-more="true"
|
||||
:to-bottom-loading-more-enabled="false"
|
||||
loading-more-default-text="点击加载更多"
|
||||
empty-view-text="暂无学生联系人"
|
||||
@query="queryStudentContactList"
|
||||
>
|
||||
<view
|
||||
v-for="item in studentContactList"
|
||||
:key="item.id"
|
||||
class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm"
|
||||
>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
学生编号:{{ item.studentId ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
名字:{{ item.name ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
简介:{{ item.description ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
出生日期:{{ item.birthday ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
性别:{{ item.sex ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
是否有效:{{ item.enabled ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
头像:{{ item.avatar ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
附件:{{ item.video ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
备注:{{ item.memo ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
创建时间:{{ item.createTime ?? '-' }}
|
||||
</view>
|
||||
<view class="mt-16rpx flex justify-end gap-16rpx">
|
||||
<wd-button size="small" variant="plain" @click="openStudentContact(item)">
|
||||
编辑
|
||||
</wd-button>
|
||||
<wd-button size="small" type="danger" variant="plain" @click="removeStudentContact(item)">
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
<!-- 子表:学生班主任 -->
|
||||
<view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx">
|
||||
<text class="text-28rpx text-[#333] font-semibold">学生班主任({{ studentTeacherTotal }})</text>
|
||||
<wd-button
|
||||
v-if="studentTeacherTotal === 0 && hasAccessByCodes(['infra:student:create'])"
|
||||
size="small" type="primary" variant="plain" @click="openStudentTeacher()"
|
||||
>
|
||||
添加
|
||||
</wd-button>
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<z-paging
|
||||
ref="studentTeacherPagingRef"
|
||||
v-model="studentTeacherList"
|
||||
:fixed="false"
|
||||
:auto="false"
|
||||
height="560rpx"
|
||||
:default-page-size="1"
|
||||
:refresher-enabled="false"
|
||||
:inside-more="true"
|
||||
:to-bottom-loading-more-enabled="false"
|
||||
loading-more-default-text="点击加载更多"
|
||||
empty-view-text="暂无学生班主任"
|
||||
@query="queryStudentTeacherList"
|
||||
>
|
||||
<view
|
||||
v-for="item in studentTeacherList"
|
||||
:key="item.id"
|
||||
class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm"
|
||||
>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
学生编号:{{ item.studentId ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
名字:{{ item.name ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
简介:{{ item.description ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
出生日期:{{ item.birthday ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
性别:{{ item.sex ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
是否有效:{{ item.enabled ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
头像:{{ item.avatar ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
附件:{{ item.video ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
备注:{{ item.memo ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
创建时间:{{ item.createTime ?? '-' }}
|
||||
</view>
|
||||
<view class="mt-16rpx flex justify-end gap-16rpx">
|
||||
<wd-button size="small" variant="plain" @click="openStudentTeacher(item)">
|
||||
编辑
|
||||
</wd-button>
|
||||
<wd-button size="small" type="danger" variant="plain" @click="removeStudentTeacher(item)">
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<view class="yd-detail-footer-actions">
|
||||
<wd-button v-if="hasAccessByCodes(['infra:student:update'])" class="flex-1" type="warning" @click="handleEdit">
|
||||
编辑
|
||||
</wd-button>
|
||||
<wd-button v-if="hasAccessByCodes(['infra:student:delete'])" class="flex-1" type="danger" :loading="deleting" @click="handleDelete">
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
Student,
|
||||
StudentContact,
|
||||
StudentTeacher,
|
||||
} from '@/api/infra/demo'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { useDialog } from '@wot-ui/ui/components/wd-dialog'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import {
|
||||
deleteStudent,
|
||||
deleteStudentContact,
|
||||
deleteStudentTeacher,
|
||||
getStudent,
|
||||
getStudentContactPage,
|
||||
getStudentTeacherPage,
|
||||
} from '@/api/infra/demo'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
|
||||
const props = defineProps<{ id?: number }>()
|
||||
|
||||
definePage({
|
||||
style: { navigationBarTitleText: '', navigationStyle: 'custom' },
|
||||
})
|
||||
|
||||
const { hasAccessByCodes } = useAccess()
|
||||
const dialog = useDialog()
|
||||
const toast = useToast()
|
||||
const formData = ref<Student>() // 主表数据
|
||||
const studentContactList = ref<StudentContact[]>([]) // 学生联系人列表
|
||||
const studentContactTotal = ref(0) // 学生联系人总数
|
||||
const studentContactPagingRef = ref<any>() // 学生联系人分页组件引用
|
||||
const studentTeacherList = ref<StudentTeacher[]>([]) // 学生班主任列表
|
||||
const studentTeacherTotal = ref(0) // 学生班主任总数
|
||||
const studentTeacherPagingRef = ref<any>() // 学生班主任分页组件引用
|
||||
const deleting = ref(false) // 主表删除状态
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-infra/demo/index')
|
||||
}
|
||||
|
||||
/** 加载主表 */
|
||||
async function loadMain() {
|
||||
if (props.id) {
|
||||
formData.value = await getStudent(props.id)
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询学生联系人列表 */
|
||||
async function queryStudentContactList(pageNo: number, pageSize: number) {
|
||||
if (!props.id) {
|
||||
studentContactPagingRef.value?.completeByTotal([], 0)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const data = await getStudentContactPage({ studentId: props.id, pageNo, pageSize })
|
||||
studentContactTotal.value = data.total
|
||||
studentContactPagingRef.value?.completeByTotal(data.list, data.total)
|
||||
} catch {
|
||||
studentContactPagingRef.value?.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开学生联系人表单 */
|
||||
function openStudentContact(item?: StudentContact) {
|
||||
const query = [
|
||||
`studentId=${props.id}`,
|
||||
item?.id ? `id=${item.id}` : '',
|
||||
].filter(Boolean).join('&')
|
||||
uni.navigateTo({ url: `/pages-infra/demo/student-contact/form/index?${query}` })
|
||||
}
|
||||
|
||||
/** 删除学生联系人 */
|
||||
async function removeStudentContact(item: StudentContact) {
|
||||
try {
|
||||
await dialog.confirm({ title: '提示', msg: '确定要删除该学生联系人吗?' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await deleteStudentContact(item.id!)
|
||||
toast.success('删除成功')
|
||||
studentContactPagingRef.value?.reload()
|
||||
}
|
||||
|
||||
/** 查询学生班主任列表 */
|
||||
async function queryStudentTeacherList(pageNo: number, pageSize: number) {
|
||||
if (!props.id) {
|
||||
studentTeacherPagingRef.value?.completeByTotal([], 0)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const data = await getStudentTeacherPage({ studentId: props.id, pageNo, pageSize })
|
||||
studentTeacherTotal.value = data.total
|
||||
studentTeacherPagingRef.value?.completeByTotal(data.list, data.total)
|
||||
} catch {
|
||||
studentTeacherPagingRef.value?.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开学生班主任表单 */
|
||||
function openStudentTeacher(item?: StudentTeacher) {
|
||||
const query = [
|
||||
`studentId=${props.id}`,
|
||||
item?.id ? `id=${item.id}` : '',
|
||||
].filter(Boolean).join('&')
|
||||
uni.navigateTo({ url: `/pages-infra/demo/student-teacher/form/index?${query}` })
|
||||
}
|
||||
|
||||
/** 删除学生班主任 */
|
||||
async function removeStudentTeacher(item: StudentTeacher) {
|
||||
try {
|
||||
await dialog.confirm({ title: '提示', msg: '确定要删除该学生班主任吗?' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await deleteStudentTeacher(item.id!)
|
||||
toast.success('删除成功')
|
||||
studentTeacherPagingRef.value?.reload()
|
||||
}
|
||||
|
||||
/** 编辑主表 */
|
||||
function handleEdit() {
|
||||
uni.navigateTo({ url: `/pages-infra/demo/form/index?id=${props.id}` })
|
||||
}
|
||||
|
||||
/** 删除主表 */
|
||||
async function handleDelete() {
|
||||
if (!props.id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await dialog.confirm({ title: '提示', msg: '确定要删除该学生吗?' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
deleting.value = true
|
||||
try {
|
||||
await deleteStudent(props.id)
|
||||
toast.success('删除成功')
|
||||
uni.$emit('infra:demo:reload')
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 进入页面加载(含编辑和子表返回后刷新) */
|
||||
onShow(() => {
|
||||
loadMain()
|
||||
nextTick(() => {
|
||||
studentContactPagingRef.value?.reload()
|
||||
studentTeacherPagingRef.value?.reload()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
-193
@@ -1,193 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
:title="getTitle"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view>
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
clearable
|
||||
placeholder="请输入名字"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
prop="birthday"
|
||||
is-link
|
||||
:value="formData.birthday ? formatDateTime(formData.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="birthdayVisible = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="birthdayVisible"
|
||||
:model-value="formData.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => formData.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
prop="sex"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center>
|
||||
<wd-radio-group v-model="formData.enabled" type="button">
|
||||
<wd-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||
<yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||
<wd-textarea
|
||||
v-model="formData.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
show-word-limit
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
</view>
|
||||
|
||||
<!-- 底部保存按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<wd-button
|
||||
type="primary"
|
||||
block
|
||||
:loading="formLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||
import type {
|
||||
Student,
|
||||
} from '@/api/infra/demo'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import { createStudent, getStudent, updateStudent } from '@/api/infra/demo'
|
||||
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import { createFormSchema } from '@/utils/wot'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
|
||||
const formLoading = ref(false) // 表单提交状态
|
||||
const formData = ref<Partial<Student>>({
|
||||
id: undefined,
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: '',
|
||||
memo: '',
|
||||
}) // 表单数据
|
||||
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||
const formSchema = createFormSchema({
|
||||
name: [{ required: true, message: '名字不能为空' }],
|
||||
description: [{ required: true, message: '简介不能为空' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||
sex: [{ required: true, message: '性别不能为空' }],
|
||||
enabled: [{ required: true, message: '是否有效不能为空' }],
|
||||
avatar: [{ required: true, message: '头像不能为空' }],
|
||||
video: [{ required: true, message: '附件不能为空' }],
|
||||
memo: [{ required: true, message: '备注不能为空' }],
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-infra/demo/index')
|
||||
}
|
||||
|
||||
/** 加载学生详情 */
|
||||
async function getDetail() {
|
||||
if (!props.id) {
|
||||
return
|
||||
}
|
||||
formData.value = await getStudent(props.id)
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value.validate()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as Student
|
||||
if (props.id) {
|
||||
await updateStudent(data)
|
||||
toast.success('修改成功')
|
||||
} else {
|
||||
await createStudent(data)
|
||||
toast.success('新增成功')
|
||||
}
|
||||
uni.$emit('infra:demo:reload')
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -1,169 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container yd-page-container-paging">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="学生管理"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 搜索组件 -->
|
||||
<SearchForm @search="handleQuery" @reset="handleReset" />
|
||||
|
||||
<!-- 学生列表 -->
|
||||
<z-paging
|
||||
ref="pagingRef"
|
||||
v-model="list"
|
||||
:fixed="false"
|
||||
class="min-h-0 flex-1"
|
||||
:default-page-size="10"
|
||||
:refresher-enabled="true"
|
||||
:inside-more="true"
|
||||
:loading-more-default-as-loading="true"
|
||||
empty-view-text="暂无学生数据"
|
||||
@query="queryList"
|
||||
>
|
||||
<view class="p-24rpx">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="p-24rpx">
|
||||
<view class="mb-16rpx flex items-center justify-between">
|
||||
<view class="text-32rpx text-[#333] font-semibold">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="item.sex" />
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">简介:</text>
|
||||
<text class="line-clamp-1">{{ item.description }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">出生日期:</text>
|
||||
<text class="line-clamp-1">{{ formatDateTime(item.birthday) || '-' }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">是否有效:</text>
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.enabled" />
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">头像:</text>
|
||||
<text class="line-clamp-1">{{ item.avatar }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">附件:</text>
|
||||
<text class="line-clamp-1">{{ item.video }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">备注:</text>
|
||||
<text class="line-clamp-1">{{ item.memo }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">创建时间:</text>
|
||||
<text class="line-clamp-1">{{ formatDateTime(item.createTime) || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
|
||||
<!-- 新增按钮 -->
|
||||
<wd-fab
|
||||
v-if="hasAccessByCodes(['infra:student:create'])"
|
||||
position="right-bottom"
|
||||
type="primary"
|
||||
:expandable="false"
|
||||
@click="handleAdd"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Student } from '@/api/infra/demo'
|
||||
import { onUnload } from '@dcloudio/uni-app'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getStudentPage } from '@/api/infra/demo'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import SearchForm from './components/search-form.vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const { hasAccessByCodes } = useAccess()
|
||||
const list = ref<Student[]>([]) // 列表数据
|
||||
const pagingRef = ref<any>() // 分页组件引用
|
||||
const queryParams = ref<Record<string, any>>({}) // 查询参数
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 查询学生列表 */
|
||||
async function queryList(pageNo: number, pageSize: number) {
|
||||
try {
|
||||
const params = {
|
||||
...queryParams.value,
|
||||
pageNo,
|
||||
pageSize,
|
||||
}
|
||||
const data = await getStudentPage(params)
|
||||
pagingRef.value?.completeByTotal(data.list, data.total)
|
||||
} catch {
|
||||
pagingRef.value?.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery(data?: Record<string, any>) {
|
||||
queryParams.value = { ...data }
|
||||
reload()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function handleReset() {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 重新加载 */
|
||||
function reload() {
|
||||
pagingRef.value?.reload()
|
||||
}
|
||||
|
||||
/** 新增学生 */
|
||||
function handleAdd() {
|
||||
uni.navigateTo({
|
||||
url: '/pages-infra/demo/form/index',
|
||||
})
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function handleDetail(item: Student) {
|
||||
uni.navigateTo({
|
||||
url: `/pages-infra/demo/detail/index?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
uni.$on('infra:demo:reload', reload)
|
||||
})
|
||||
|
||||
/** 卸载 */
|
||||
onUnload(() => {
|
||||
uni.$off('infra:demo:reload', reload)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<view @click="visible = true">
|
||||
<wd-search :placeholder="placeholder" hide-cancel disabled />
|
||||
</view>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup v-model="visible" position="top" @close="visible = false">
|
||||
<view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }">
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
名字
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
出生日期
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.birthday"
|
||||
placeholder="请输入出生日期"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<yd-search-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
:columns="getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
all-option
|
||||
/>
|
||||
<yd-search-picker
|
||||
v-model="formData.enabled"
|
||||
label="是否有效"
|
||||
:columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
all-option
|
||||
/>
|
||||
<yd-search-date-range v-model="formData.createTime" label="创建时间" />
|
||||
<view class="yd-search-form-actions">
|
||||
<wd-button class="flex-1" plain @click="handleReset">
|
||||
重置
|
||||
</wd-button>
|
||||
<wd-button class="flex-1" type="primary" @click="handleSearch">
|
||||
搜索
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { getNavbarHeight } from '@/utils'
|
||||
import { formatDate, formatDateRange } from '@/utils/date'
|
||||
import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
|
||||
const emit = defineEmits<{
|
||||
search: [data: Record<string, any>]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = ref(false) // 搜索弹窗显示状态
|
||||
const formData = reactive({
|
||||
name: undefined as string | undefined,
|
||||
birthday: undefined as string | undefined,
|
||||
sex: undefined as number | undefined,
|
||||
enabled: undefined as boolean | undefined,
|
||||
createTime: [undefined, undefined] as [number | undefined, number | undefined],
|
||||
}) // 搜索表单数据
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const placeholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (formData.name) {
|
||||
conditions.push(`名字:${formData.name}`)
|
||||
}
|
||||
if (formData.birthday) {
|
||||
conditions.push(`出生日期:${formData.birthday}`)
|
||||
}
|
||||
if (formData.sex !== undefined) {
|
||||
conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`)
|
||||
}
|
||||
if (formData.enabled !== undefined) {
|
||||
conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`)
|
||||
}
|
||||
if (formData.createTime?.[0] && formData.createTime?.[1]) {
|
||||
conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索学生'
|
||||
})
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleSearch() {
|
||||
visible.value = false
|
||||
emit('search', {
|
||||
...formData,
|
||||
createTime: formatDateRange(formData.createTime),
|
||||
})
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function handleReset() {
|
||||
formData.name = undefined
|
||||
formData.birthday = undefined
|
||||
formData.sex = undefined
|
||||
formData.enabled = undefined
|
||||
formData.createTime = [undefined, undefined]
|
||||
visible.value = false
|
||||
emit('reset')
|
||||
}
|
||||
</script>
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar :title="getTitle" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" />
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||
<wd-input v-model="formData.name" clearable placeholder="请输入名字" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
prop="birthday"
|
||||
is-link
|
||||
:value="formData.birthday ? formatDateTime(formData.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="birthdayVisible = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="birthdayVisible"
|
||||
:model-value="formData.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => formData.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
prop="sex"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.enabled"
|
||||
label="是否有效"
|
||||
label-width="180rpx"
|
||||
prop="enabled"
|
||||
:columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
placeholder="请选择是否有效"
|
||||
/>
|
||||
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||
<yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||
<wd-textarea
|
||||
v-model="formData.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
|
||||
<!-- 底部保存按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<wd-button type="primary" block :loading="formLoading" @click="handleSubmit">
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||
import type { StudentContact } from '@/api/infra/demo'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import { createStudentContact, getStudentContact, updateStudentContact } from '@/api/infra/demo'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import { createFormSchema } from '@/utils/wot'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
studentId: number
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: { navigationBarTitleText: '', navigationStyle: 'custom' },
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const getTitle = computed(() => props.id ? '编辑学生联系人' : '新增学生联系人')
|
||||
const formLoading = ref(false) // 表单提交状态
|
||||
const formData = ref<Partial<StudentContact>>({
|
||||
id: undefined,
|
||||
studentId: props.studentId,
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: undefined,
|
||||
memo: '',
|
||||
}) // 表单数据
|
||||
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||
const formSchema = createFormSchema({
|
||||
studentId: [{ required: true, message: '学生编号不能为空' }],
|
||||
name: [{ required: true, message: '名字不能为空' }],
|
||||
description: [{ required: true, message: '简介不能为空' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||
sex: [{ required: true, message: '性别不能为空' }],
|
||||
enabled: [{ required: true, message: '是否有效不能为空' }],
|
||||
avatar: [{ required: true, message: '头像不能为空' }],
|
||||
memo: [{ required: true, message: '备注不能为空' }],
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 加载学生联系人详情 */
|
||||
async function getDetail() {
|
||||
if (props.id) {
|
||||
formData.value = await getStudentContact(props.id)
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value.validate()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = {
|
||||
...formData.value,
|
||||
studentId: props.studentId ?? formData.value.studentId,
|
||||
} as StudentContact
|
||||
if (props.id) {
|
||||
await updateStudentContact(data)
|
||||
toast.success('修改成功')
|
||||
} else {
|
||||
await createStudentContact(data)
|
||||
toast.success('新增成功')
|
||||
}
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar :title="getTitle" left-arrow placeholder safe-area-inset-top fixed @click-left="handleBack" />
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||
<wd-input v-model="formData.name" clearable placeholder="请输入名字" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
prop="birthday"
|
||||
is-link
|
||||
:value="formData.birthday ? formatDateTime(formData.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="birthdayVisible = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="birthdayVisible"
|
||||
:model-value="formData.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => formData.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
prop="sex"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.enabled"
|
||||
label="是否有效"
|
||||
label-width="180rpx"
|
||||
prop="enabled"
|
||||
:columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
placeholder="请选择是否有效"
|
||||
/>
|
||||
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||
<yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||
<wd-textarea
|
||||
v-model="formData.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
|
||||
<!-- 底部保存按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<wd-button type="primary" block :loading="formLoading" @click="handleSubmit">
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||
import type { StudentTeacher } from '@/api/infra/demo'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import { createStudentTeacher, getStudentTeacher, updateStudentTeacher } from '@/api/infra/demo'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import { createFormSchema } from '@/utils/wot'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
studentId: number
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: { navigationBarTitleText: '', navigationStyle: 'custom' },
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const getTitle = computed(() => props.id ? '编辑学生班主任' : '新增学生班主任')
|
||||
const formLoading = ref(false) // 表单提交状态
|
||||
const formData = ref<Partial<StudentTeacher>>({
|
||||
id: undefined,
|
||||
studentId: props.studentId,
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: undefined,
|
||||
memo: '',
|
||||
}) // 表单数据
|
||||
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||
const formSchema = createFormSchema({
|
||||
studentId: [{ required: true, message: '学生编号不能为空' }],
|
||||
name: [{ required: true, message: '名字不能为空' }],
|
||||
description: [{ required: true, message: '简介不能为空' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||
sex: [{ required: true, message: '性别不能为空' }],
|
||||
enabled: [{ required: true, message: '是否有效不能为空' }],
|
||||
avatar: [{ required: true, message: '头像不能为空' }],
|
||||
memo: [{ required: true, message: '备注不能为空' }],
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 加载学生班主任详情 */
|
||||
async function getDetail() {
|
||||
if (props.id) {
|
||||
formData.value = await getStudentTeacher(props.id)
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value.validate()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = {
|
||||
...formData.value,
|
||||
studentId: props.studentId ?? formData.value.studentId,
|
||||
} as StudentTeacher
|
||||
if (props.id) {
|
||||
await updateStudentTeacher(data)
|
||||
toast.success('修改成功')
|
||||
} else {
|
||||
await createStudentTeacher(data)
|
||||
toast.success('新增成功')
|
||||
}
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
[ {
|
||||
"contentPath" : "java/InfraStudentPageReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentRespVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentSaveReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentController",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
|
||||
}, {
|
||||
"contentPath" : "xml/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImpl",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentService",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImplTest",
|
||||
"filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
|
||||
}, {
|
||||
"contentPath" : "java/ErrorCodeConstants_手动操作",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
|
||||
}, {
|
||||
"contentPath" : "sql/sql",
|
||||
"filePath" : "sql/sql.sql"
|
||||
}, {
|
||||
"contentPath" : "sql/h2",
|
||||
"filePath" : "sql/h2.sql"
|
||||
}, {
|
||||
"contentPath" : "ts/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"
|
||||
}, {
|
||||
"contentPath" : "vue/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/search-form",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/form/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/detail/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"
|
||||
} ]
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
|
||||
// ========== 学生 TODO 补充编号 ==========
|
||||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生联系人 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student_contact")
|
||||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentContactDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 学生联系人 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
|
||||
|
||||
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
|
||||
return selectList(InfraStudentContactDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentId(Long studentId) {
|
||||
return delete(InfraStudentContactDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentIds(List<Long> studentIds) {
|
||||
return deleteBatch(InfraStudentContactDO::getStudentId, studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.validation.*;
|
||||
import jakarta.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
|
||||
|
||||
@Tag(name = "管理后台 - 学生")
|
||||
@RestController
|
||||
@RequestMapping("/infra/student")
|
||||
@Validated
|
||||
public class InfraStudentController {
|
||||
|
||||
@Resource
|
||||
private InfraStudentService studentService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:create')")
|
||||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
|
||||
return success(studentService.createStudent(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:update')")
|
||||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
|
||||
studentService.updateStudent(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除学生")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
|
||||
studentService.deleteStudent(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) {
|
||||
studentService.deleteStudentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得学生")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
|
||||
InfraStudentDO student = studentService.getStudent(id);
|
||||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得学生分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
|
||||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出学生 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
|
||||
BeanUtils.toBean(list, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
@GetMapping("/student-contact/list-by-student-id")
|
||||
@Operation(summary = "获得学生联系人列表")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentContactListByStudentId(studentId));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
@GetMapping("/student-teacher/get-by-student-id")
|
||||
@Operation(summary = "获得学生班主任")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentTeacherByStudentId(studentId));
|
||||
}
|
||||
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student")
|
||||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
|
||||
/**
|
||||
* 学生 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
|
||||
|
||||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
|
||||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
|
||||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
|
||||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
|
||||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
|
||||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(InfraStudentDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 学生分页 Request VO")
|
||||
@Data
|
||||
public class InfraStudentPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "名字", example = "芋头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", example = "true")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.idev.excel.annotation.*;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
|
||||
@Schema(description = "管理后台 - 学生 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class InfraStudentRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@ExcelProperty("名字")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@ExcelProperty("简介")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty(value = "性别", converter = DictConvert.class)
|
||||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
|
||||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@ExcelProperty("头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@ExcelProperty("附件")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@ExcelProperty("备注")
|
||||
private String memo;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
|
||||
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
|
||||
@Data
|
||||
public class InfraStudentSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@NotEmpty(message = "名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@NotEmpty(message = "简介不能为空")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "出生日期不能为空")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "性别不能为空")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否有效不能为空")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@NotEmpty(message = "头像不能为空")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@NotEmpty(message = "附件不能为空")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@NotEmpty(message = "备注不能为空")
|
||||
private String memo;
|
||||
|
||||
@Schema(description = "学生联系人列表")
|
||||
private List<InfraStudentContactDO> studentContacts;
|
||||
|
||||
@Schema(description = "学生班主任")
|
||||
private InfraStudentTeacherDO studentTeacher;
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import java.util.*;
|
||||
import jakarta.validation.*;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 学生 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface InfraStudentService {
|
||||
|
||||
/**
|
||||
* 创建学生
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createStudent(@Valid InfraStudentSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新学生
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除学生
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteStudent(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除学生
|
||||
*
|
||||
* @param ids 编号
|
||||
*/
|
||||
void deleteStudentListByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得学生
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 学生
|
||||
*/
|
||||
InfraStudentDO getStudent(Long id);
|
||||
|
||||
/**
|
||||
* 获得学生分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 学生分页
|
||||
*/
|
||||
PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO);
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
/**
|
||||
* 获得学生联系人列表
|
||||
*
|
||||
* @param studentId 学生编号
|
||||
* @return 学生联系人列表
|
||||
*/
|
||||
List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId);
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
/**
|
||||
* 获得学生班主任
|
||||
*
|
||||
* @param studentId 学生编号
|
||||
* @return 学生班主任
|
||||
*/
|
||||
InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId);
|
||||
|
||||
}
|
||||
-191
@@ -1,191 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 学生 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class InfraStudentServiceImpl implements InfraStudentService {
|
||||
|
||||
@Resource
|
||||
private InfraStudentMapper studentMapper;
|
||||
@Resource
|
||||
private InfraStudentContactMapper studentContactMapper;
|
||||
@Resource
|
||||
private InfraStudentTeacherMapper studentTeacherMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createStudent(InfraStudentSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class);
|
||||
studentMapper.insert(student);
|
||||
|
||||
|
||||
// 插入子表
|
||||
createStudentContactList(student.getId(), createReqVO.getStudentContacts());
|
||||
createStudentTeacher(student.getId(), createReqVO.getStudentTeacher());
|
||||
// 返回
|
||||
return student.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStudent(InfraStudentSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateStudentExists(updateReqVO.getId());
|
||||
// 更新
|
||||
InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class);
|
||||
studentMapper.updateById(updateObj);
|
||||
|
||||
// 更新子表
|
||||
updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts());
|
||||
updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteStudent(Long id) {
|
||||
// 校验存在
|
||||
validateStudentExists(id);
|
||||
// 删除
|
||||
studentMapper.deleteById(id);
|
||||
|
||||
// 删除子表
|
||||
deleteStudentContactByStudentId(id);
|
||||
deleteStudentTeacherByStudentId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteStudentListByIds(List<Long> ids) {
|
||||
// 删除
|
||||
studentMapper.deleteByIds(ids);
|
||||
|
||||
// 删除子表
|
||||
deleteStudentContactByStudentIds(ids);
|
||||
deleteStudentTeacherByStudentIds(ids);
|
||||
}
|
||||
|
||||
|
||||
private void validateStudentExists(Long id) {
|
||||
if (studentMapper.selectById(id) == null) {
|
||||
throw exception(STUDENT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfraStudentDO getStudent(Long id) {
|
||||
return studentMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<InfraStudentDO> getStudentPage(InfraStudentPageReqVO pageReqVO) {
|
||||
return studentMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
@Override
|
||||
public List<InfraStudentContactDO> getStudentContactListByStudentId(Long studentId) {
|
||||
return studentContactMapper.selectListByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void createStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
list.forEach(o -> o.setStudentId(studentId).clean());
|
||||
studentContactMapper.insertBatch(list);
|
||||
}
|
||||
|
||||
private void updateStudentContactList(Long studentId, List<InfraStudentContactDO> list) {
|
||||
list.forEach(o -> o.setStudentId(studentId).clean());
|
||||
List<InfraStudentContactDO> oldList = studentContactMapper.selectListByStudentId(studentId);
|
||||
List<List<InfraStudentContactDO>> diffList = diffList(oldList, list, (oldVal, newVal) -> {
|
||||
boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId());
|
||||
if (same) {
|
||||
newVal.setId(oldVal.getId()).clean(); // 解决更新情况下:updateTime 不更新
|
||||
}
|
||||
return same;
|
||||
});
|
||||
|
||||
// 第二步,批量添加、修改、删除
|
||||
if (CollUtil.isNotEmpty(diffList.get(0))) {
|
||||
studentContactMapper.insertBatch(diffList.get(0));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(1))) {
|
||||
studentContactMapper.updateBatch(diffList.get(1));
|
||||
}
|
||||
if (CollUtil.isNotEmpty(diffList.get(2))) {
|
||||
studentContactMapper.deleteByIds(convertList(diffList.get(2), InfraStudentContactDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteStudentContactByStudentId(Long studentId) {
|
||||
studentContactMapper.deleteByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void deleteStudentContactByStudentIds(List<Long> studentIds) {
|
||||
studentContactMapper.deleteByStudentIds(studentIds);
|
||||
}
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
@Override
|
||||
public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) {
|
||||
return studentTeacherMapper.selectByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
|
||||
if (studentTeacher == null) {
|
||||
return;
|
||||
}
|
||||
studentTeacher.setStudentId(studentId);
|
||||
studentTeacherMapper.insert(studentTeacher);
|
||||
}
|
||||
|
||||
private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) {
|
||||
if (studentTeacher == null) {
|
||||
return;
|
||||
}
|
||||
studentTeacher.setStudentId(studentId).clean();// 解决更新情况下:updateTime 不更新
|
||||
studentTeacherMapper.insertOrUpdate(studentTeacher);
|
||||
}
|
||||
|
||||
private void deleteStudentTeacherByStudentId(Long studentId) {
|
||||
studentTeacherMapper.deleteByStudentId(studentId);
|
||||
}
|
||||
|
||||
private void deleteStudentTeacherByStudentIds(List<Long> studentIds) {
|
||||
studentTeacherMapper.deleteByStudentIds(studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.service.demo;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link InfraStudentServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Import(InfraStudentServiceImpl.class)
|
||||
public class InfraStudentServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private InfraStudentServiceImpl studentService;
|
||||
|
||||
@Resource
|
||||
private InfraStudentMapper studentMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateStudent_success() {
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null);
|
||||
|
||||
// 调用
|
||||
Long studentId = studentService.createStudent(createReqVO);
|
||||
// 断言
|
||||
assertNotNull(studentId);
|
||||
// 校验记录的属性是否正确
|
||||
InfraStudentDO student = studentMapper.selectById(studentId);
|
||||
assertPojoEquals(createReqVO, student, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateStudent_success() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
|
||||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> {
|
||||
o.setId(dbStudent.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
studentService.updateStudent(updateReqVO);
|
||||
// 校验是否更新正确
|
||||
InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(updateReqVO, student);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateStudent_notExists() {
|
||||
// 准备参数
|
||||
InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteStudent_success() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class);
|
||||
studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbStudent.getId();
|
||||
|
||||
// 调用
|
||||
studentService.deleteStudent(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(studentMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteStudent_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetStudentPage() {
|
||||
// mock 数据
|
||||
InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到
|
||||
o.setName(null);
|
||||
o.setBirthday(null);
|
||||
o.setSex(null);
|
||||
o.setEnabled(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
studentMapper.insert(dbStudent);
|
||||
// 测试 name 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null)));
|
||||
// 测试 birthday 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null)));
|
||||
// 测试 sex 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null)));
|
||||
// 测试 enabled 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null)));
|
||||
// 测试 createTime 不匹配
|
||||
studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO();
|
||||
reqVO.setName(null);
|
||||
reqVO.setBirthday(null);
|
||||
reqVO.setSex(null);
|
||||
reqVO.setEnabled(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbStudent, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生班主任 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student_teacher")
|
||||
@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentTeacherDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 学生班主任 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentTeacherMapper extends BaseMapperX<InfraStudentTeacherDO> {
|
||||
|
||||
default InfraStudentTeacherDO selectByStudentId(Long studentId) {
|
||||
return selectOne(InfraStudentTeacherDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentId(Long studentId) {
|
||||
return delete(InfraStudentTeacherDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentIds(List<Long> studentIds) {
|
||||
return deleteBatch(InfraStudentTeacherDO::getStudentId, studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里
|
||||
CREATE TABLE IF NOT EXISTS "infra_student" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar NOT NULL,
|
||||
"description" varchar NOT NULL,
|
||||
"birthday" varchar NOT NULL,
|
||||
"sex" int NOT NULL,
|
||||
"enabled" bit NOT NULL,
|
||||
"avatar" varchar NOT NULL,
|
||||
"video" varchar NOT NULL,
|
||||
"memo" varchar NOT NULL,
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT '学生表';
|
||||
|
||||
-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里
|
||||
DELETE FROM "infra_student";
|
||||
@@ -1,54 +0,0 @@
|
||||
-- 菜单 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status, component_name
|
||||
)
|
||||
VALUES (
|
||||
'学生管理', '', 2, 0, 888,
|
||||
'student', '', 'infra/demo/index', 0, 'InfraStudent'
|
||||
);
|
||||
|
||||
-- 按钮父菜单ID
|
||||
SELECT @parentId := LAST_INSERT_ID();
|
||||
|
||||
-- 按钮 SQL
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生查询', 'infra:student:query', 3, 1, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生创建', 'infra:student:create', 3, 2, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生更新', 'infra:student:update', 3, 3, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生删除', 'infra:student:delete', 3, 4, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
INSERT INTO system_menu(
|
||||
name, permission, type, sort, parent_id,
|
||||
path, icon, component, status
|
||||
)
|
||||
VALUES (
|
||||
'学生导出', 'infra:student:export', 3, 5, @parentId,
|
||||
'', '', '', 0
|
||||
);
|
||||
@@ -1,87 +0,0 @@
|
||||
import type { PageParam, PageResult } from '@/http/types'
|
||||
import { http } from '@/http/http'
|
||||
|
||||
/** 学生联系人信息 */
|
||||
export interface StudentContact {
|
||||
id?: number
|
||||
studentId?: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video?: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
/** 学生班主任信息 */
|
||||
export interface StudentTeacher {
|
||||
id?: number
|
||||
studentId?: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video?: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
}
|
||||
|
||||
/** 学生信息 */
|
||||
export interface Student {
|
||||
id?: number
|
||||
name: string
|
||||
description: string
|
||||
birthday: number
|
||||
sex: number
|
||||
enabled: boolean
|
||||
avatar: string
|
||||
video: string
|
||||
memo: string
|
||||
createTime?: number
|
||||
studentContacts?: StudentContact[]
|
||||
studentTeacher?: StudentTeacher | null
|
||||
}
|
||||
|
||||
/** 获取学生分页列表 */
|
||||
export function getStudentPage(params: PageParam) {
|
||||
return http.get<PageResult<Student>>('/infra/student/page', params)
|
||||
}
|
||||
|
||||
/** 获取学生详情 */
|
||||
export function getStudent(id: number) {
|
||||
return http.get<Student>(`/infra/student/get?id=${id}`)
|
||||
}
|
||||
|
||||
/** 创建学生 */
|
||||
export function createStudent(data: Student) {
|
||||
return http.post<number>('/infra/student/create', data)
|
||||
}
|
||||
|
||||
/** 更新学生 */
|
||||
export function updateStudent(data: Student) {
|
||||
return http.put<boolean>('/infra/student/update', data)
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
export function deleteStudent(id: number) {
|
||||
return http.delete<boolean>(`/infra/student/delete?id=${id}`)
|
||||
}
|
||||
|
||||
// ==================== 子表:学生联系人 ====================
|
||||
|
||||
/** 获取学生联系人列表 */
|
||||
export function getStudentContactListByStudentId(studentId: number) {
|
||||
return http.get<StudentContact[]>(`/infra/student/student-contact/list-by-student-id?studentId=${studentId}`)
|
||||
}
|
||||
|
||||
// ==================== 子表:学生班主任 ====================
|
||||
|
||||
/** 获取学生班主任 */
|
||||
export function getStudentTeacherByStudentId(studentId: number) {
|
||||
return http.get<StudentTeacher | null>(`/infra/student/student-teacher/get-by-student-id?studentId=${studentId}`)
|
||||
}
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="学生详情"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<view class="pb-160rpx">
|
||||
<!-- 主表详情 -->
|
||||
<wd-cell-group border title="学生信息">
|
||||
<wd-cell title="编号" :value="formData?.id ?? '-'" />
|
||||
<wd-cell title="名字" :value="formData?.name ?? '-'" />
|
||||
<wd-cell title="简介" :value="formData?.description ?? '-'" />
|
||||
<wd-cell title="出生日期" :value="formatDateTime(formData?.birthday) || '-'" />
|
||||
<wd-cell title="性别">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="formData?.sex" />
|
||||
</wd-cell>
|
||||
<wd-cell title="是否有效">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="formData?.enabled" />
|
||||
</wd-cell>
|
||||
<wd-cell title="头像" :value="formData?.avatar ?? '-'" />
|
||||
<wd-cell title="附件" :value="formData?.video ?? '-'" />
|
||||
<wd-cell title="备注" :value="formData?.memo ?? '-'" />
|
||||
<wd-cell title="创建时间" :value="formatDateTime(formData?.createTime) || '-'" />
|
||||
</wd-cell-group>
|
||||
|
||||
<!-- 子表:学生联系人 -->
|
||||
<view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]">
|
||||
学生联系人({{ studentContacts.length }})
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]">
|
||||
暂无学生联系人
|
||||
</view>
|
||||
<view
|
||||
v-for="item in studentContacts"
|
||||
:key="item.id"
|
||||
class="mb-16rpx rounded-12rpx bg-white p-24rpx shadow-sm"
|
||||
>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
学生编号:{{ item.studentId ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
名字:{{ item.name ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
简介:{{ item.description ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
出生日期:{{ item.birthday ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
性别:{{ item.sex ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
是否有效:{{ item.enabled ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
头像:{{ item.avatar ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
附件:{{ item.video ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
备注:{{ item.memo ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
创建时间:{{ item.createTime ?? '-' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 子表:学生班主任 -->
|
||||
<view class="mt-20rpx px-24rpx py-16rpx text-28rpx text-[#999]">
|
||||
学生班主任
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<view class="rounded-12rpx bg-white p-24rpx shadow-sm">
|
||||
<template v-if="studentTeacher">
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
学生编号:{{ studentTeacher.studentId ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
名字:{{ studentTeacher.name ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
简介:{{ studentTeacher.description ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
出生日期:{{ studentTeacher.birthday ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
性别:{{ studentTeacher.sex ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
是否有效:{{ studentTeacher.enabled ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
头像:{{ studentTeacher.avatar ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
附件:{{ studentTeacher.video ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
备注:{{ studentTeacher.memo ?? '-' }}
|
||||
</view>
|
||||
<view class="mb-8rpx text-28rpx text-[#666]">
|
||||
创建时间:{{ studentTeacher.createTime ?? '-' }}
|
||||
</view>
|
||||
</template>
|
||||
<view v-else class="text-center text-26rpx text-[#999]">
|
||||
暂无学生班主任
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<view class="yd-detail-footer-actions">
|
||||
<wd-button
|
||||
v-if="hasAccessByCodes(['infra:student:update'])"
|
||||
class="flex-1" type="warning" @click="handleEdit"
|
||||
>
|
||||
编辑
|
||||
</wd-button>
|
||||
<wd-button
|
||||
v-if="hasAccessByCodes(['infra:student:delete'])"
|
||||
class="flex-1" type="danger" :loading="deleting" @click="handleDelete"
|
||||
>
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type {
|
||||
Student,
|
||||
StudentContact,
|
||||
StudentTeacher,
|
||||
} from '@/api/infra/demo'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { ref } from 'vue'
|
||||
import { useDialog } from '@wot-ui/ui/components/wd-dialog'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import {
|
||||
deleteStudent,
|
||||
getStudent,
|
||||
getStudentContactListByStudentId,
|
||||
getStudentTeacherByStudentId,
|
||||
} from '@/api/infra/demo'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const { hasAccessByCodes } = useAccess()
|
||||
const dialog = useDialog()
|
||||
const toast = useToast()
|
||||
const formData = ref<Student>() // 主表数据
|
||||
const studentContacts = ref<StudentContact[]>([]) // 学生联系人数据
|
||||
const studentTeacher = ref<StudentTeacher | null>(null) // 学生班主任数据
|
||||
const deleting = ref(false) // 删除状态
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-infra/demo/index')
|
||||
}
|
||||
|
||||
/** 加载主表和子表详情 */
|
||||
async function getDetail() {
|
||||
if (!props.id || deleting.value) {
|
||||
return
|
||||
}
|
||||
const [data, studentContactData, studentTeacherData] = await Promise.all([
|
||||
getStudent(props.id),
|
||||
getStudentContactListByStudentId(props.id),
|
||||
getStudentTeacherByStudentId(props.id),
|
||||
])
|
||||
formData.value = data
|
||||
studentContacts.value = studentContactData || []
|
||||
studentTeacher.value = studentTeacherData || null
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
function handleEdit() {
|
||||
uni.navigateTo({ url: `/pages-infra/demo/form/index?id=${props.id}` })
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
async function handleDelete() {
|
||||
if (!props.id) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await dialog.confirm({ title: '提示', msg: '确定要删除该学生吗?' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
deleting.value = true
|
||||
try {
|
||||
await deleteStudent(props.id)
|
||||
toast.success('删除成功')
|
||||
uni.$emit('infra:demo:reload')
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 进入页面加载(含编辑返回后刷新) */
|
||||
onShow(() => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
-480
@@ -1,480 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
:title="getTitle"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view>
|
||||
<wd-form ref="formRef" :model="formData" :schema="formSchema">
|
||||
<wd-cell-group border>
|
||||
<wd-form-item title="名字" title-width="180rpx" prop="name">
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
clearable
|
||||
placeholder="请输入名字"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx" prop="description">
|
||||
<wd-textarea
|
||||
v-model="formData.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
show-word-limit
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
prop="birthday"
|
||||
is-link
|
||||
:value="formData.birthday ? formatDateTime(formData.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="birthdayVisible = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="birthdayVisible"
|
||||
:model-value="formData.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => formData.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
prop="sex"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<wd-form-item title="是否有效" title-width="180rpx" prop="enabled" center>
|
||||
<wd-radio-group v-model="formData.enabled" type="button">
|
||||
<wd-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="头像" title-width="180rpx" prop="avatar">
|
||||
<yd-upload-img v-model="formData.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx" prop="video">
|
||||
<yd-upload-file v-model="formData.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx" prop="memo">
|
||||
<wd-textarea
|
||||
v-model="formData.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
show-word-limit
|
||||
/>
|
||||
</wd-form-item>
|
||||
</wd-cell-group>
|
||||
</wd-form>
|
||||
|
||||
<!-- 子表:学生联系人 -->
|
||||
<view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx">
|
||||
<text class="text-28rpx text-[#333] font-semibold">学生联系人</text>
|
||||
<wd-button size="small" type="primary" variant="plain" @click="addStudentContact">
|
||||
添加
|
||||
</wd-button>
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<view v-if="!studentContacts.length" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]">
|
||||
暂无学生联系人
|
||||
</view>
|
||||
<view
|
||||
v-for="(item, index) in studentContacts"
|
||||
:key="index"
|
||||
class="mb-20rpx rounded-12rpx bg-white p-24rpx shadow-sm"
|
||||
>
|
||||
<view class="mb-16rpx flex justify-end">
|
||||
<wd-button size="small" type="danger" variant="plain" @click="removeStudentContact(index)">
|
||||
删除
|
||||
</wd-button>
|
||||
</view>
|
||||
<wd-form-item title="名字" title-width="180rpx">
|
||||
<wd-input v-model="item.name" clearable placeholder="请输入名字" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx">
|
||||
<wd-textarea
|
||||
v-model="item.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
is-link
|
||||
:value="item.birthday ? formatDateTime(item.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="subDateVisible[`studentContacts.${index}.birthday`] = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="subDateVisible[`studentContacts.${index}.birthday`]"
|
||||
:model-value="item.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => item.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="item.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<wd-form-item title="是否有效" title-width="180rpx" center>
|
||||
<wd-radio-group v-model="item.enabled" type="button">
|
||||
<wd-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="头像" title-width="180rpx">
|
||||
<yd-upload-img v-model="item.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx">
|
||||
<yd-upload-file v-model="item.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx">
|
||||
<wd-textarea
|
||||
v-model="item.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 子表:学生班主任 -->
|
||||
<view class="mt-20rpx flex items-center justify-between px-24rpx py-16rpx">
|
||||
<text class="text-28rpx text-[#333] font-semibold">学生班主任</text>
|
||||
<wd-button
|
||||
v-if="!studentTeacher"
|
||||
size="small"
|
||||
type="primary"
|
||||
variant="plain"
|
||||
@click="addStudentTeacher()"
|
||||
>
|
||||
添加
|
||||
</wd-button>
|
||||
</view>
|
||||
<view class="px-24rpx">
|
||||
<view v-if="!studentTeacher" class="rounded-12rpx bg-white py-40rpx text-center text-26rpx text-[#999]">
|
||||
暂无学生班主任
|
||||
</view>
|
||||
<view v-else class="rounded-12rpx bg-white p-24rpx shadow-sm">
|
||||
<wd-form-item title="名字" title-width="180rpx">
|
||||
<wd-input v-model="studentTeacher.name" clearable placeholder="请输入名字" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="简介" title-width="180rpx">
|
||||
<wd-textarea
|
||||
v-model="studentTeacher.description"
|
||||
clearable
|
||||
placeholder="请输入简介"
|
||||
:maxlength="200"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="出生日期"
|
||||
title-width="180rpx"
|
||||
is-link
|
||||
:value="studentTeacher.birthday ? formatDateTime(studentTeacher.birthday) : ''"
|
||||
placeholder="请选择出生日期"
|
||||
@click="subDateVisible['studentTeacher.birthday'] = true"
|
||||
/>
|
||||
<wd-datetime-picker
|
||||
v-model:visible="subDateVisible['studentTeacher.birthday']"
|
||||
:model-value="studentTeacher.birthday || Date.now()"
|
||||
type="datetime"
|
||||
@update:model-value="value => studentTeacher.birthday = value"
|
||||
/>
|
||||
<yd-form-picker
|
||||
v-model="studentTeacher.sex"
|
||||
label="性别"
|
||||
label-width="180rpx"
|
||||
:dict-type="DICT_TYPE.SYSTEM_USER_SEX"
|
||||
placeholder="请选择性别"
|
||||
/>
|
||||
<wd-form-item title="是否有效" title-width="180rpx" center>
|
||||
<wd-radio-group v-model="studentTeacher.enabled" type="button">
|
||||
<wd-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
<wd-form-item title="头像" title-width="180rpx">
|
||||
<yd-upload-img v-model="studentTeacher.avatar" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="附件" title-width="180rpx">
|
||||
<yd-upload-file v-model="studentTeacher.video" :limit="1" directory="infra/demo" />
|
||||
</wd-form-item>
|
||||
<wd-form-item title="备注" title-width="180rpx">
|
||||
<wd-textarea
|
||||
v-model="studentTeacher.memo"
|
||||
clearable
|
||||
placeholder="请输入备注"
|
||||
:maxlength="2000"
|
||||
/>
|
||||
</wd-form-item>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部保存按钮 -->
|
||||
<view class="yd-detail-footer">
|
||||
<wd-button
|
||||
type="primary"
|
||||
block
|
||||
:loading="formLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
保存
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { FormInstance } from '@wot-ui/ui/components/wd-form/types'
|
||||
import type {
|
||||
Student,
|
||||
StudentContact,
|
||||
StudentTeacher,
|
||||
} from '@/api/infra/demo'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useToast } from '@wot-ui/ui/components/wd-toast'
|
||||
import {
|
||||
createStudent,
|
||||
getStudent,
|
||||
getStudentContactListByStudentId,
|
||||
getStudentTeacherByStudentId,
|
||||
updateStudent,
|
||||
} from '@/api/infra/demo'
|
||||
import { getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { delay, navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import { createFormSchema } from '@/utils/wot'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: number
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const toast = useToast()
|
||||
const getTitle = computed(() => props.id ? '编辑学生' : '新增学生')
|
||||
const formLoading = ref(false) // 表单提交状态
|
||||
const formData = ref<Partial<Student>>({
|
||||
id: undefined,
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: '',
|
||||
memo: '',
|
||||
}) // 表单数据
|
||||
const studentContacts = ref<Partial<StudentContact>[]>([]) // 学生联系人数据
|
||||
const studentTeacher = ref<Partial<StudentTeacher> | null>(null) // 学生班主任数据
|
||||
const birthdayVisible = ref(false) // 出生日期选择器状态
|
||||
const subDateVisible = ref<Record<string, boolean>>({}) // 子表日期选择器状态
|
||||
const formSchema = createFormSchema({
|
||||
name: [{ required: true, message: '名字不能为空' }],
|
||||
description: [{ required: true, message: '简介不能为空' }],
|
||||
birthday: [{ required: true, message: '出生日期不能为空' }],
|
||||
sex: [{ required: true, message: '性别不能为空' }],
|
||||
enabled: [{ required: true, message: '是否有效不能为空' }],
|
||||
avatar: [{ required: true, message: '头像不能为空' }],
|
||||
video: [{ required: true, message: '附件不能为空' }],
|
||||
memo: [{ required: true, message: '备注不能为空' }],
|
||||
}) // 表单校验规则
|
||||
const formRef = ref<FormInstance>() // 表单组件引用
|
||||
|
||||
/** 添加学生联系人 */
|
||||
function addStudentContact() {
|
||||
studentContacts.value.push({
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: undefined,
|
||||
memo: '',
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除学生联系人 */
|
||||
function removeStudentContact(index: number) {
|
||||
studentContacts.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 添加学生班主任 */
|
||||
function addStudentTeacher() {
|
||||
studentTeacher.value = {
|
||||
name: '',
|
||||
description: '',
|
||||
birthday: undefined,
|
||||
sex: undefined,
|
||||
enabled: undefined,
|
||||
avatar: '',
|
||||
video: undefined,
|
||||
memo: '',
|
||||
}
|
||||
}
|
||||
|
||||
/** 校验主子表数据 */
|
||||
function isEmptySubValue(value: unknown) {
|
||||
return value === undefined || value === null || value === ''
|
||||
}
|
||||
|
||||
function validateSubTables() {
|
||||
for (let index = 0; index < studentContacts.value.length; index++) {
|
||||
const item = studentContacts.value[index]!
|
||||
if (isEmptySubValue(item.name)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的名字不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.description)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的简介不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.birthday)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的出生日期不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.sex)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的性别不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.enabled)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的是否有效不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.avatar)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的头像不能为空`)
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(item.memo)) {
|
||||
toast.warning(`学生联系人第${index + 1}项的备注不能为空`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if (studentTeacher.value) {
|
||||
if (isEmptySubValue(studentTeacher.value.name)) {
|
||||
toast.warning('学生班主任的名字不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.description)) {
|
||||
toast.warning('学生班主任的简介不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.birthday)) {
|
||||
toast.warning('学生班主任的出生日期不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.sex)) {
|
||||
toast.warning('学生班主任的性别不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.enabled)) {
|
||||
toast.warning('学生班主任的是否有效不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.avatar)) {
|
||||
toast.warning('学生班主任的头像不能为空')
|
||||
return false
|
||||
}
|
||||
if (isEmptySubValue(studentTeacher.value.memo)) {
|
||||
toast.warning('学生班主任的备注不能为空')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus('/pages-infra/demo/index')
|
||||
}
|
||||
|
||||
/** 加载学生详情 */
|
||||
async function getDetail() {
|
||||
if (!props.id) {
|
||||
return
|
||||
}
|
||||
const [data, studentContactData, studentTeacherData] = await Promise.all([
|
||||
getStudent(props.id),
|
||||
getStudentContactListByStudentId(props.id),
|
||||
getStudentTeacherByStudentId(props.id),
|
||||
])
|
||||
formData.value = data
|
||||
studentContacts.value = studentContactData || []
|
||||
studentTeacher.value = studentTeacherData || null
|
||||
}
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid } = await formRef.value.validate()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
if (!validateSubTables()) {
|
||||
return
|
||||
}
|
||||
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = {
|
||||
...formData.value,
|
||||
studentContacts: studentContacts.value,
|
||||
studentTeacher: studentTeacher.value,
|
||||
} as Student
|
||||
if (props.id) {
|
||||
await updateStudent(data)
|
||||
toast.success('修改成功')
|
||||
} else {
|
||||
await createStudent(data)
|
||||
toast.success('新增成功')
|
||||
}
|
||||
uni.$emit('infra:demo:reload')
|
||||
delay(handleBack)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
getDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
<template>
|
||||
<view class="yd-page-container yd-page-container-paging">
|
||||
<!-- 顶部导航栏 -->
|
||||
<wd-navbar
|
||||
title="学生管理"
|
||||
left-arrow placeholder safe-area-inset-top fixed
|
||||
@click-left="handleBack"
|
||||
/>
|
||||
|
||||
<!-- 搜索组件 -->
|
||||
<SearchForm @search="handleQuery" @reset="handleReset" />
|
||||
|
||||
<!-- 学生列表 -->
|
||||
<z-paging
|
||||
ref="pagingRef"
|
||||
v-model="list"
|
||||
:fixed="false"
|
||||
class="min-h-0 flex-1"
|
||||
:default-page-size="10"
|
||||
:refresher-enabled="true"
|
||||
:inside-more="true"
|
||||
:loading-more-default-as-loading="true"
|
||||
empty-view-text="暂无学生数据"
|
||||
@query="queryList"
|
||||
>
|
||||
<view class="p-24rpx">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="p-24rpx">
|
||||
<view class="mb-16rpx flex items-center justify-between">
|
||||
<view class="text-32rpx text-[#333] font-semibold">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="item.sex" />
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">简介:</text>
|
||||
<text class="line-clamp-1">{{ item.description }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">出生日期:</text>
|
||||
<text class="line-clamp-1">{{ formatDateTime(item.birthday) || '-' }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">是否有效:</text>
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="item.enabled" />
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">头像:</text>
|
||||
<text class="line-clamp-1">{{ item.avatar }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">附件:</text>
|
||||
<text class="line-clamp-1">{{ item.video }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">备注:</text>
|
||||
<text class="line-clamp-1">{{ item.memo }}</text>
|
||||
</view>
|
||||
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
|
||||
<text class="mr-8rpx text-[#999]">创建时间:</text>
|
||||
<text class="line-clamp-1">{{ formatDateTime(item.createTime) || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
|
||||
<!-- 新增按钮 -->
|
||||
<wd-fab
|
||||
v-if="hasAccessByCodes(['infra:student:create'])"
|
||||
position="right-bottom"
|
||||
type="primary"
|
||||
:expandable="false"
|
||||
@click="handleAdd"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { Student } from '@/api/infra/demo'
|
||||
import { onUnload } from '@dcloudio/uni-app'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { getStudentPage } from '@/api/infra/demo'
|
||||
import { useAccess } from '@/hooks/useAccess'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDateTime } from '@/utils/date'
|
||||
import SearchForm from './components/search-form.vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
})
|
||||
|
||||
const { hasAccessByCodes } = useAccess()
|
||||
const list = ref<Student[]>([]) // 列表数据
|
||||
const pagingRef = ref<any>() // 分页组件引用
|
||||
const queryParams = ref<Record<string, any>>({}) // 查询参数
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
navigateBackPlus()
|
||||
}
|
||||
|
||||
/** 查询学生列表 */
|
||||
async function queryList(pageNo: number, pageSize: number) {
|
||||
try {
|
||||
const params = {
|
||||
...queryParams.value,
|
||||
pageNo,
|
||||
pageSize,
|
||||
}
|
||||
const data = await getStudentPage(params)
|
||||
pagingRef.value?.completeByTotal(data.list, data.total)
|
||||
} catch {
|
||||
pagingRef.value?.complete(false)
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery(data?: Record<string, any>) {
|
||||
queryParams.value = { ...data }
|
||||
reload()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function handleReset() {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 重新加载 */
|
||||
function reload() {
|
||||
pagingRef.value?.reload()
|
||||
}
|
||||
|
||||
/** 新增学生 */
|
||||
function handleAdd() {
|
||||
uni.navigateTo({
|
||||
url: '/pages-infra/demo/form/index',
|
||||
})
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function handleDetail(item: Student) {
|
||||
uni.navigateTo({
|
||||
url: `/pages-infra/demo/detail/index?id=${item.id}`,
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
uni.$on('infra:demo:reload', reload)
|
||||
})
|
||||
|
||||
/** 卸载 */
|
||||
onUnload(() => {
|
||||
uni.$off('infra:demo:reload', reload)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<view @click="visible = true">
|
||||
<wd-search :placeholder="placeholder" hide-cancel disabled />
|
||||
</view>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup v-model="visible" position="top" @close="visible = false">
|
||||
<view class="yd-search-form-container" :style="{ paddingTop: `${getNavbarHeight()}px` }">
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
名字
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入名字"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<view class="yd-search-form-item">
|
||||
<view class="yd-search-form-label">
|
||||
出生日期
|
||||
</view>
|
||||
<wd-input
|
||||
v-model="formData.birthday"
|
||||
placeholder="请输入出生日期"
|
||||
clearable
|
||||
/>
|
||||
</view>
|
||||
<yd-search-picker
|
||||
v-model="formData.sex"
|
||||
label="性别"
|
||||
:columns="getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
all-option
|
||||
/>
|
||||
<yd-search-picker
|
||||
v-model="formData.enabled"
|
||||
label="是否有效"
|
||||
:columns="getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
all-option
|
||||
/>
|
||||
<yd-search-date-range v-model="formData.createTime" label="创建时间" />
|
||||
<view class="yd-search-form-actions">
|
||||
<wd-button class="flex-1" plain @click="handleReset">
|
||||
重置
|
||||
</wd-button>
|
||||
<wd-button class="flex-1" type="primary" @click="handleSearch">
|
||||
搜索
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { getNavbarHeight } from '@/utils'
|
||||
import { formatDate, formatDateRange } from '@/utils/date'
|
||||
import { getDictLabel, getIntDictOptions, getBoolDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
|
||||
const emit = defineEmits<{
|
||||
search: [data: Record<string, any>]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = ref(false) // 搜索弹窗显示状态
|
||||
const formData = reactive({
|
||||
name: undefined as string | undefined,
|
||||
birthday: undefined as string | undefined,
|
||||
sex: undefined as number | undefined,
|
||||
enabled: undefined as boolean | undefined,
|
||||
createTime: [undefined, undefined] as [number | undefined, number | undefined],
|
||||
}) // 搜索表单数据
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const placeholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (formData.name) {
|
||||
conditions.push(`名字:${formData.name}`)
|
||||
}
|
||||
if (formData.birthday) {
|
||||
conditions.push(`出生日期:${formData.birthday}`)
|
||||
}
|
||||
if (formData.sex !== undefined) {
|
||||
conditions.push(`性别:${getDictLabel(DICT_TYPE.SYSTEM_USER_SEX, formData.sex)}`)
|
||||
}
|
||||
if (formData.enabled !== undefined) {
|
||||
conditions.push(`是否有效:${getDictLabel(DICT_TYPE.INFRA_BOOLEAN_STRING, formData.enabled)}`)
|
||||
}
|
||||
if (formData.createTime?.[0] && formData.createTime?.[1]) {
|
||||
conditions.push(`创建时间:${formatDate(formData.createTime[0])}~${formatDate(formData.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索学生'
|
||||
})
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleSearch() {
|
||||
visible.value = false
|
||||
emit('search', {
|
||||
...formData,
|
||||
createTime: formatDateRange(formData.createTime),
|
||||
})
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function handleReset() {
|
||||
formData.name = undefined
|
||||
formData.birthday = undefined
|
||||
formData.sex = undefined
|
||||
formData.enabled = undefined
|
||||
formData.createTime = [undefined, undefined]
|
||||
visible.value = false
|
||||
emit('reset')
|
||||
}
|
||||
</script>
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
<?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="cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
[ {
|
||||
"contentPath" : "java/InfraStudentPageReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentRespVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentSaveReqVO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentController",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherDO",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentContactMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentTeacherMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java"
|
||||
}, {
|
||||
"contentPath" : "xml/InfraStudentMapper",
|
||||
"filePath" : "yudao-module-infra/src/main/resources/mapper/demo/InfraStudentMapper.xml"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImpl",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentService",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java"
|
||||
}, {
|
||||
"contentPath" : "java/InfraStudentServiceImplTest",
|
||||
"filePath" : "yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java"
|
||||
}, {
|
||||
"contentPath" : "java/ErrorCodeConstants_手动操作",
|
||||
"filePath" : "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java"
|
||||
}, {
|
||||
"contentPath" : "sql/sql",
|
||||
"filePath" : "sql/sql.sql"
|
||||
}, {
|
||||
"contentPath" : "sql/h2",
|
||||
"filePath" : "sql/h2.sql"
|
||||
}, {
|
||||
"contentPath" : "ts/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/api/infra/demo/index.ts"
|
||||
}, {
|
||||
"contentPath" : "vue/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/search-form",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/components/search-form.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/form/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/form/index.vue"
|
||||
}, {
|
||||
"contentPath" : "vue/detail/index",
|
||||
"filePath" : "yudao-ui-admin-uniapp/src/pages-infra/demo/detail/index.vue"
|
||||
} ]
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// TODO 待办:请将下面的错误码复制到 yudao-module-infra 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!!
|
||||
// ========== 学生 TODO 补充编号 ==========
|
||||
ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在");
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生联系人 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student_contact")
|
||||
@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentContactDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 学生联系人 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentContactMapper extends BaseMapperX<InfraStudentContactDO> {
|
||||
|
||||
default List<InfraStudentContactDO> selectListByStudentId(Long studentId) {
|
||||
return selectList(InfraStudentContactDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentId(Long studentId) {
|
||||
return delete(InfraStudentContactDO::getStudentId, studentId);
|
||||
}
|
||||
|
||||
default int deleteByStudentIds(List<Long> studentIds) {
|
||||
return deleteBatch(InfraStudentContactDO::getStudentId, studentIds);
|
||||
}
|
||||
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import jakarta.validation.*;
|
||||
import jakarta.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService;
|
||||
|
||||
@Tag(name = "管理后台 - 学生")
|
||||
@RestController
|
||||
@RequestMapping("/infra/student")
|
||||
@Validated
|
||||
public class InfraStudentController {
|
||||
|
||||
@Resource
|
||||
private InfraStudentService studentService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:create')")
|
||||
public CommonResult<Long> createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) {
|
||||
return success(studentService.createStudent(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:update')")
|
||||
public CommonResult<Boolean> updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) {
|
||||
studentService.updateStudent(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除学生")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudent(@RequestParam("id") Long id) {
|
||||
studentService.deleteStudent(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号", required = true)
|
||||
@Operation(summary = "批量删除学生")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:delete')")
|
||||
public CommonResult<Boolean> deleteStudentList(@RequestParam("ids") List<Long> ids) {
|
||||
studentService.deleteStudentListByIds(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得学生")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentRespVO> getStudent(@RequestParam("id") Long id) {
|
||||
InfraStudentDO student = studentService.getStudent(id);
|
||||
return success(BeanUtils.toBean(student, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得学生分页")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<PageResult<InfraStudentRespVO>> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) {
|
||||
PageResult<InfraStudentDO> pageResult = studentService.getStudentPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出学生 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<InfraStudentDO> list = studentService.getStudentPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class,
|
||||
BeanUtils.toBean(list, InfraStudentRespVO.class));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生联系人) ====================
|
||||
|
||||
@GetMapping("/student-contact/list-by-student-id")
|
||||
@Operation(summary = "获得学生联系人列表")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<List<InfraStudentContactDO>> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentContactListByStudentId(studentId));
|
||||
}
|
||||
|
||||
// ==================== 子表(学生班主任) ====================
|
||||
|
||||
@GetMapping("/student-teacher/get-by-student-id")
|
||||
@Operation(summary = "获得学生班主任")
|
||||
@Parameter(name = "studentId", description = "学生编号")
|
||||
@PreAuthorize("@ss.hasPermission('infra:student:query')")
|
||||
public CommonResult<InfraStudentTeacherDO> getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) {
|
||||
return success(studentService.getStudentTeacherByStudentId(studentId));
|
||||
}
|
||||
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.dataobject.demo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 学生 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_student")
|
||||
@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InfraStudentDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 是否有效
|
||||
*
|
||||
* 枚举 {@link TODO infra_boolean_string 对应的类}
|
||||
*/
|
||||
private Boolean enabled;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private String video;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String memo;
|
||||
|
||||
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.dal.mysql.demo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*;
|
||||
|
||||
/**
|
||||
* 学生 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface InfraStudentMapper extends BaseMapperX<InfraStudentDO> {
|
||||
|
||||
default PageResult<InfraStudentDO> selectPage(InfraStudentPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InfraStudentDO>()
|
||||
.likeIfPresent(InfraStudentDO::getName, reqVO.getName())
|
||||
.eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday())
|
||||
.eqIfPresent(InfraStudentDO::getSex, reqVO.getSex())
|
||||
.eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled())
|
||||
.betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(InfraStudentDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 学生分页 Request VO")
|
||||
@Data
|
||||
public class InfraStudentPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "名字", example = "芋头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", example = "1")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", example = "true")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.idev.excel.annotation.*;
|
||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||
|
||||
@Schema(description = "管理后台 - 学生 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class InfraStudentRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@ExcelProperty("名字")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@ExcelProperty("简介")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("出生日期")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@ExcelProperty(value = "性别", converter = DictConvert.class)
|
||||
@DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@ExcelProperty(value = "是否有效", converter = DictConvert.class)
|
||||
@DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@ExcelProperty("头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@ExcelProperty("附件")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@ExcelProperty("备注")
|
||||
private String memo;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.demo.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO;
|
||||
|
||||
@Schema(description = "管理后台 - 学生新增/修改 Request VO")
|
||||
@Data
|
||||
public class InfraStudentSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头")
|
||||
@NotEmpty(message = "名字不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍")
|
||||
@NotEmpty(message = "简介不能为空")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "出生日期不能为空")
|
||||
private LocalDateTime birthday;
|
||||
|
||||
@Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "性别不能为空")
|
||||
private Integer sex;
|
||||
|
||||
@Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否有效不能为空")
|
||||
private Boolean enabled;
|
||||
|
||||
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
|
||||
@NotEmpty(message = "头像不能为空")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4")
|
||||
@NotEmpty(message = "附件不能为空")
|
||||
private String video;
|
||||
|
||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注")
|
||||
@NotEmpty(message = "备注不能为空")
|
||||
private String memo;
|
||||
|
||||
@Schema(description = "学生联系人列表")
|
||||
private List<InfraStudentContactDO> studentContacts;
|
||||
|
||||
@Schema(description = "学生班主任")
|
||||
private InfraStudentTeacherDO studentTeacher;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user