Compare commits
No commits in common. "0d390100b46dbb94efbcaccc4ed6a126e29f0af7" and "04a08d5327c9bb0f5a27bd67dd3db025bab65fe2" have entirely different histories.
0d390100b4
...
04a08d5327
|
|
@ -1,515 +0,0 @@
|
||||||
# 用户志愿控制器 API 接口文档
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
用户志愿控制器 (UserVolunteerController) 提供了志愿管理的相关接口,包括志愿保存、详情查询、名称修改、列表查询、删除和切换功能。
|
|
||||||
|
|
||||||
**基础路径**: `/api/user/volunteer`
|
|
||||||
|
|
||||||
**认证方式**: 需要登录认证,通过 JWT Token 进行身份验证
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 保存志愿明细
|
|
||||||
|
|
||||||
保存用户选择的志愿专业列表到当前激活的志愿表中。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `POST`
|
|
||||||
- **路径**: `/api/user/volunteer/save`
|
|
||||||
- **Content-Type**: `application/json`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 说明 |
|
|
||||||
|--------|--------|------|----------------------------------------|
|
|
||||||
| keys | string[] | 是 | 志愿专业Key列表,格式:`学校代码_专业代码_招生代码` |
|
|
||||||
|
|
||||||
**请求示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"10001_1001_001",
|
|
||||||
"10002_2001_002",
|
|
||||||
"10003_3001_003"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | string | 响应数据 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": "保存成功"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 500,
|
|
||||||
"message": "未找到计算表名",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|----------------------------|
|
|
||||||
| 500 | 获取用户成绩信息失败 |
|
|
||||||
| 500 | 未找到计算表名 |
|
|
||||||
| 500 | 查找志愿表失败 |
|
|
||||||
| 500 | 请先创建志愿表 |
|
|
||||||
| 500 | 查找专业信息失败 |
|
|
||||||
| 500 | 删除旧数据失败 |
|
|
||||||
| 500 | 保存失败 |
|
|
||||||
|
|
||||||
**业务逻辑**
|
|
||||||
|
|
||||||
1. 对传入的keys进行去重处理
|
|
||||||
2. 获取当前登录用户的激活成绩信息
|
|
||||||
3. 查找当前激活的志愿表
|
|
||||||
4. 根据keys查找对应的专业信息
|
|
||||||
5. 构建志愿记录列表,保持提交顺序
|
|
||||||
6. 先删除旧的志愿记录,再批量插入新记录
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 获取当前志愿单详情
|
|
||||||
|
|
||||||
获取当前激活志愿单的详细信息,包括志愿记录按批次分组展示。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `GET`
|
|
||||||
- **路径**: `/api/user/volunteer/detail`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
无
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|--------------------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | object | 志愿详情数据 |
|
|
||||||
|
|
||||||
**data 数据结构**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|------------|--------|--------------------------|
|
|
||||||
| volunteer | object | 志愿单基本信息 |
|
|
||||||
| items | object | 按批次分组的志愿明细 |
|
|
||||||
|
|
||||||
**items 数据结构**
|
|
||||||
|
|
||||||
| 批次 | 类型 | 说明 |
|
|
||||||
|--------|------|------|
|
|
||||||
| 提前批 | array | 提前批志愿记录列表 |
|
|
||||||
| 本科批 | array | 本科批志愿记录列表 |
|
|
||||||
| 专科批 | array | 专科批志愿记录列表 |
|
|
||||||
|
|
||||||
**志愿记录项数据结构**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|------------------------|---------|--------------------------------|
|
|
||||||
| volunteerID | string | 志愿单ID |
|
|
||||||
| schoolCode | string | 学校代码 |
|
|
||||||
| majorCode | string | 专业代码 |
|
|
||||||
| enrollmentCode | string | 招生代码 |
|
|
||||||
| indexs | int | 志愿序号 |
|
|
||||||
| batch | string | 批次 |
|
|
||||||
| enrollProbability | float64 | 录取概率 |
|
|
||||||
| studentConvertedScore | float64 | 学生折合分 |
|
|
||||||
| calculationMajorID | string | 计算专业ID |
|
|
||||||
| schoolName | string | 学校名称 |
|
|
||||||
| majorName | string | 专业名称 |
|
|
||||||
| planNum | int | 计划人数 |
|
|
||||||
| tuition | string | 学费 |
|
|
||||||
| province | string | 省份 |
|
|
||||||
| schoolNature | string | 院校性质 |
|
|
||||||
| institutionType | string | 院校类型 |
|
|
||||||
| majorDetail | string | 专业详情 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": {
|
|
||||||
"volunteer": {
|
|
||||||
"id": "123456789",
|
|
||||||
"volunteerName": "我的志愿表",
|
|
||||||
"scoreId": "987654321",
|
|
||||||
"createType": 1,
|
|
||||||
"state": 1
|
|
||||||
},
|
|
||||||
"items": {
|
|
||||||
"提前批": [
|
|
||||||
{
|
|
||||||
"volunteerID": "123456789",
|
|
||||||
"schoolCode": "10001",
|
|
||||||
"majorCode": "1001",
|
|
||||||
"enrollmentCode": "001",
|
|
||||||
"indexs": 1,
|
|
||||||
"batch": "本科提前批",
|
|
||||||
"schoolName": "某某大学",
|
|
||||||
"majorName": "美术学",
|
|
||||||
"planNum": 30,
|
|
||||||
"tuition": "8000元/年",
|
|
||||||
"enrollProbability": 85.5
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"本科批": [],
|
|
||||||
"专科批": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|------------------------|
|
|
||||||
| 500 | 获取用户成绩信息失败 |
|
|
||||||
| 500 | 查找志愿表失败 |
|
|
||||||
| 500 | 查找志愿明细失败 |
|
|
||||||
|
|
||||||
**批次分类规则**
|
|
||||||
|
|
||||||
- **提前批**: `提前批`、`本科提前批`
|
|
||||||
- **专科批**: `高职高专`、`专科批`
|
|
||||||
- **本科批**: 其他所有批次
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 编辑志愿单名称
|
|
||||||
|
|
||||||
修改志愿单的名称。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `PUT`
|
|
||||||
- **路径**: `/api/user/volunteer/updateName`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 位置 | 说明 |
|
|
||||||
|--------|--------|------|--------|------------|
|
|
||||||
| id | string | 是 | query | 志愿单ID |
|
|
||||||
| name | string | 是 | query | 志愿单名称 |
|
|
||||||
|
|
||||||
**请求示例**
|
|
||||||
|
|
||||||
```
|
|
||||||
PUT /api/user/volunteer/updateName?id=123456789&name=我的新志愿表
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | string | 响应数据 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": "更新成功"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "参数错误",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|----------------|
|
|
||||||
| 400 | 参数错误 |
|
|
||||||
| 500 | 更新失败 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 获取当前用户志愿单列表
|
|
||||||
|
|
||||||
分页查询当前用户的志愿单列表。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `GET`
|
|
||||||
- **路径**: `/api/user/volunteer/list`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 位置 | 默认值 | 说明 |
|
|
||||||
|--------|------|------|-------|--------|--------|
|
|
||||||
| page | int | 否 | query | 1 | 页码 |
|
|
||||||
| size | int | 否 | query | 10 | 每页数量 |
|
|
||||||
|
|
||||||
**请求示例**
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/user/volunteer/list?page=1&size=10
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | object | 响应数据 |
|
|
||||||
|
|
||||||
**data 数据结构**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|--------|--------|--------------|
|
|
||||||
| items | array | 志愿单列表 |
|
|
||||||
| total | int64 | 总数量 |
|
|
||||||
|
|
||||||
**志愿单数据结构**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------------|---------|------------------------------|
|
|
||||||
| id | string | 志愿单ID |
|
|
||||||
| volunteerName | string | 志愿单名称 |
|
|
||||||
| scoreId | string | 关联成绩ID |
|
|
||||||
| createType | int | 生成类型 (1.手动, 2.智能) |
|
|
||||||
| state | string | 状态 (0.未使用, 1.使用中, 2.历史) |
|
|
||||||
| createBy | string | 创建人 |
|
|
||||||
| createTime | string | 创建时间 |
|
|
||||||
| updateBy | string | 更新人 |
|
|
||||||
| updateTime | string | 更新时间 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": {
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "123456789",
|
|
||||||
"volunteerName": "我的志愿表",
|
|
||||||
"scoreId": "987654321",
|
|
||||||
"createType": 1,
|
|
||||||
"state": "1",
|
|
||||||
"createBy": "user001",
|
|
||||||
"createTime": "2026-01-31T10:00:00Z"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|----------------|
|
|
||||||
| 500 | 查询失败 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 删除志愿单
|
|
||||||
|
|
||||||
删除指定的志愿单。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `DELETE`
|
|
||||||
- **路径**: `/api/user/volunteer/delete`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 位置 | 说明 |
|
|
||||||
|--------|--------|------|-------|----------|
|
|
||||||
| id | string | 是 | query | 志愿单ID |
|
|
||||||
|
|
||||||
**请求示例**
|
|
||||||
|
|
||||||
```
|
|
||||||
DELETE /api/user/volunteer/delete?id=123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | string | 响应数据 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": "删除成功"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "参数错误",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|----------------|
|
|
||||||
| 400 | 参数错误 |
|
|
||||||
| 500 | 删除失败 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 切换当前志愿单
|
|
||||||
|
|
||||||
切换当前激活的志愿单。
|
|
||||||
|
|
||||||
**请求**
|
|
||||||
|
|
||||||
- **方法**: `POST`
|
|
||||||
- **路径**: `/api/user/volunteer/switch`
|
|
||||||
|
|
||||||
**请求参数**
|
|
||||||
|
|
||||||
| 参数名 | 类型 | 必填 | 位置 | 说明 |
|
|
||||||
|--------|--------|------|-------|----------|
|
|
||||||
| id | string | 是 | query | 志愿单ID |
|
|
||||||
|
|
||||||
**请求示例**
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/user/volunteer/switch?id=123456789
|
|
||||||
```
|
|
||||||
|
|
||||||
**响应**
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 说明 |
|
|
||||||
|----------|--------|----------|
|
|
||||||
| code | int | 状态码 |
|
|
||||||
| message | string | 响应消息 |
|
|
||||||
| data | string | 响应数据 |
|
|
||||||
|
|
||||||
**成功响应示例**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": "切换成功"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**特殊响应示例(已是当前志愿单)**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": "已是当前志愿单,忽略切换"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**错误码说明**
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|----------------------------|
|
|
||||||
| 400 | 参数错误 |
|
|
||||||
| 500 | 获取用户成绩信息失败 |
|
|
||||||
| 500 | 切换失败 |
|
|
||||||
|
|
||||||
**业务逻辑**
|
|
||||||
|
|
||||||
1. 检查当前是否已是该志愿单,如果是则忽略切换
|
|
||||||
2. 执行志愿单切换操作
|
|
||||||
3. Redis 缓存同步(由 Service 层处理)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 通用响应格式说明
|
|
||||||
|
|
||||||
### 成功响应
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 200,
|
|
||||||
"message": "success",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 错误响应
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 错误码,
|
|
||||||
"message": "错误信息",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 常见错误码
|
|
||||||
|
|
||||||
| 错误码 | 说明 |
|
|
||||||
|--------|--------------------|
|
|
||||||
| 200 | 成功 |
|
|
||||||
| 400 | 请求参数错误 |
|
|
||||||
| 500 | 服务器内部错误 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 认证说明
|
|
||||||
|
|
||||||
所有接口都需要在请求头中携带 JWT Token:
|
|
||||||
|
|
||||||
```
|
|
||||||
Authorization: Bearer {token}
|
|
||||||
```
|
|
||||||
|
|
||||||
Token 从登录接口获取,有效期24小时。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **保存志愿明细**: 每次保存会先删除当前志愿表中的所有记录,再插入新记录
|
|
||||||
2. **批次分类**: 志愿详情按 提前批、本科批、专科批 三大类分组展示
|
|
||||||
3. **数据去重**: 保存志愿时会自动去重,避免重复添加相同专业
|
|
||||||
4. **顺序保持**: 志愿序号按照提交的 keys 顺序依次递增
|
|
||||||
5. **权限控制**: 所有操作都基于当前登录用户的身份,只能操作自己的志愿数据
|
|
||||||
|
|
@ -1,515 +0,0 @@
|
||||||
# Entity、DTO、VO 使用问题分析与改进建议
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
本文档分析了项目中 Entity、DTO、VO 的使用情况,指出了存在的不规范使用问题,并提供了改进建议。
|
|
||||||
|
|
||||||
## 一、标准规范
|
|
||||||
|
|
||||||
### 1.1 定义与职责
|
|
||||||
|
|
||||||
| 类型 | 全称 | 职责 | 位置 | 示例 |
|
|
||||||
|------|------|------|------|------|
|
|
||||||
| Entity | Entity | 数据库表映射实体,与数据库表一一对应 | modules/xxx/entity/ | YxUserScore |
|
|
||||||
| DTO | Data Transfer Object | 数据传输对象,用于接口请求/响应的数据传递 | modules/xxx/dto/ | SaveScoreRequest |
|
|
||||||
| VO | View Object | 视图对象,用于前端展示,可能聚合多个Entity数据 | modules/xxx/vo/ | UserScoreVO |
|
|
||||||
|
|
||||||
### 1.2 分层调用规范
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────┐
|
|
||||||
│ Controller │ ← 接收DTO,返回VO
|
|
||||||
└──────┬──────┘
|
|
||||||
│ DTO入参,VO出参
|
|
||||||
┌──────▼──────┐
|
|
||||||
│ Service │ ← 处理业务逻辑,使用Entity进行数据操作
|
|
||||||
└──────┬──────┘
|
|
||||||
│ Entity操作
|
|
||||||
┌──────▼──────┐
|
|
||||||
│ Mapper │ ← 直接操作Entity与数据库
|
|
||||||
└─────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 二、当前使用问题分析
|
|
||||||
|
|
||||||
### 2.1 System 模块问题
|
|
||||||
|
|
||||||
#### 问题1:Controller 直接接收和返回 Entity ❌
|
|
||||||
|
|
||||||
**文件**: `server/modules/system/controller/sys_user_controller.go`
|
|
||||||
|
|
||||||
| 接口 | 问题 | 问题描述 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `POST /sys-users` | 接收Entity | Create方法直接使用 `entity.SysUser` 绑定JSON请求 |
|
|
||||||
| `PUT /sys-users/:id` | 接收Entity | Update方法直接使用 `entity.SysUser` 绑定JSON请求 |
|
|
||||||
| `GET /sys-users` | 返回Entity | List方法直接返回 `[]entity.SysUser` |
|
|
||||||
| `GET /sys-users/:id` | 返回Entity | GetByID方法直接返回 `entity.SysUser` |
|
|
||||||
|
|
||||||
**代码示例**:
|
|
||||||
```go
|
|
||||||
// ❌ 错误做法
|
|
||||||
func (ctrl *SysUserController) Create(c *gin.Context) {
|
|
||||||
var item entity.SysUser // 直接使用Entity绑定请求
|
|
||||||
if err := c.ShouldBindJSON(&item); err != nil {
|
|
||||||
common.Error(c, 400, "参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ 正确做法(建议)
|
|
||||||
type CreateUserRequest struct {
|
|
||||||
Username string `json:"username" binding:"required"`
|
|
||||||
Realname string `json:"realname" binding:"required"`
|
|
||||||
Password string `json:"password" binding:"required"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctrl *SysUserController) Create(c *gin.Context) {
|
|
||||||
var req CreateUserRequest // 使用DTO绑定请求
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
common.Error(c, 400, "参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Service层处理DTO到Entity的转换
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 问题2:LoginUser 应该是 VO 而不是 Entity ❌
|
|
||||||
|
|
||||||
**文件**: `server/modules/system/entity/sys_user.go`
|
|
||||||
|
|
||||||
```go
|
|
||||||
// ❌ 当前:LoginUser定义在entity包中
|
|
||||||
type LoginUser struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Realname string `json:"realname"`
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进建议**: `LoginUser` 应该移至 `server/modules/system/vo/login_user_vo.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.2 User 模块问题
|
|
||||||
|
|
||||||
#### 问题3:Controller 返回数据不一致 ⚠️
|
|
||||||
|
|
||||||
**文件**: `server/modules/user/controller/user_score_controller.go`
|
|
||||||
|
|
||||||
| 接口 | 返回类型 | 评价 |
|
|
||||||
|------|----------|------|
|
|
||||||
| `GET /user/score` | `vo.UserScoreVO` | ✅ 正确,返回VO |
|
|
||||||
| `GET /user/score/:id` | `interface{}` | ⚠️ 模糊,Service返回 `vo.UserScoreVO` 但Controller使用 `interface{}` 接收 |
|
|
||||||
| `GET /user/score/list` | `[]vo.UserScoreVO` | ✅ 正确,返回VO |
|
|
||||||
|
|
||||||
**代码示例**:
|
|
||||||
```go
|
|
||||||
// ⚠️ 当前做法
|
|
||||||
func (ctrl *UserScoreController) GetByID(c *gin.Context) {
|
|
||||||
id := c.Param("id")
|
|
||||||
item, err := ctrl.userScoreService.GetByID(id) // 返回 interface{}
|
|
||||||
if err != nil {
|
|
||||||
common.Error(c, 404, "记录不存在")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
common.Success(c, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Service层应该明确返回类型
|
|
||||||
func (s *UserScoreService) GetByID(id string) (vo.UserScoreVO, error) {
|
|
||||||
var entity entity.YxUserScore
|
|
||||||
// 查询逻辑...
|
|
||||||
return s.convertEntityToVo(entity), nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 问题4:UserVolunteerController 的 GetVolunteerDetail 使用匿名结构体 ⚠️
|
|
||||||
|
|
||||||
**文件**: `server/modules/user/controller/user_volunteer_controller.go:127`
|
|
||||||
|
|
||||||
```go
|
|
||||||
// ⚠️ 当前做法:在Controller中定义匿名结构体
|
|
||||||
type VolunteerDetailItem struct {
|
|
||||||
entity.YxVolunteerRecord
|
|
||||||
SchoolName string `json:"schoolName"`
|
|
||||||
MajorName string `json:"majorName"`
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进建议**: 应该定义在 `server/modules/user/vo/volunteer_detail_vo.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.3 Yx 模块问题
|
|
||||||
|
|
||||||
#### 问题5:YxVolunteerController 和 YxCalculationMajorController 大量使用 Entity ❌
|
|
||||||
|
|
||||||
**文件**:
|
|
||||||
- `server/modules/yx/controller/yx_volunteer_controller.go`
|
|
||||||
- `server/modules/yx/controller/yx_calculation_major_controller.go`
|
|
||||||
|
|
||||||
| 接口 | 问题 | 严重程度 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `POST /yx-volunteers` | 接收Entity | 🔴 高 |
|
|
||||||
| `PUT /yx-volunteers/:id` | 接收Entity | 🔴 高 |
|
|
||||||
| `GET /yx-volunteers` | 返回Entity | 🟡 中 |
|
|
||||||
| `POST /yx-calculation-majors` | 接收Entity | 🔴 高 |
|
|
||||||
| `PUT /yx-calculation-majors/:id` | 接收Entity | 🔴 高 |
|
|
||||||
|
|
||||||
**代码示例**:
|
|
||||||
```go
|
|
||||||
// ❌ 错误做法
|
|
||||||
func (ctrl *YxVolunteerController) Create(c *gin.Context) {
|
|
||||||
var item entity.YxVolunteer // 直接使用Entity
|
|
||||||
if err := c.ShouldBindJSON(&item); err != nil {
|
|
||||||
common.Error(c, 400, "参数错误")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 问题6:SchoolMajorDTO 混合了Entity和计算字段 ⚠️
|
|
||||||
|
|
||||||
**文件**: `server/modules/yx/dto/yx_school_major_dto.go:23`
|
|
||||||
|
|
||||||
```go
|
|
||||||
type SchoolMajorDTO struct {
|
|
||||||
// ... 基础字段
|
|
||||||
HistoryMajorEnrollList []entity.YxHistoryMajorEnroll `json:"historyMajorEnrollList"` // ❌ DTO中嵌套Entity
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**改进建议**: 创建 `YxHistoryMajorEnrollVO` 替代
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、改进方案
|
|
||||||
|
|
||||||
### 3.1 System 模块改进
|
|
||||||
|
|
||||||
#### 需要创建的文件
|
|
||||||
|
|
||||||
```
|
|
||||||
server/modules/system/
|
|
||||||
├── dto/
|
|
||||||
│ ├── sys_user_dto.go (新建)
|
|
||||||
│ └── auth_dto.go (已存在,需检查)
|
|
||||||
└── vo/
|
|
||||||
├── sys_user_vo.go (新建)
|
|
||||||
└── login_user_vo.go (新建,从entity迁移)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 需要修改的接口
|
|
||||||
|
|
||||||
| Controller方法 | 当前 | 改进后 |
|
|
||||||
|----------------|------|--------|
|
|
||||||
| Create | 接收Entity | 接收CreateUserRequest DTO |
|
|
||||||
| Update | 接收Entity | 接收UpdateUserRequest DTO |
|
|
||||||
| List | 返回Entity | 返回[]SysUserVO |
|
|
||||||
| GetByID | 返回Entity | 返回SysUserVO |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.2 User 模块改进
|
|
||||||
|
|
||||||
#### 需要创建的文件
|
|
||||||
|
|
||||||
```
|
|
||||||
server/modules/user/
|
|
||||||
├── dto/
|
|
||||||
│ └── user_volunteer_dto.go (新建)
|
|
||||||
└── vo/
|
|
||||||
├── volunteer_detail_vo.go (新建)
|
|
||||||
└── volunteer_record_vo.go (新建)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 需要修改的接口
|
|
||||||
|
|
||||||
| Controller方法 | 当前 | 改进后 |
|
|
||||||
|----------------|------|--------|
|
|
||||||
| UserScoreController.GetByID | 返回interface{} | 返回UserScoreVO |
|
|
||||||
| UserVolunteerController.GetVolunteerDetail | 使用匿名结构体 | 使用VolunteerDetailVO |
|
|
||||||
| UserVolunteerController.SaveVolunteer | 接收[]string | 创建SaveVolunteerRequest DTO |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3.3 Yx 模块改进
|
|
||||||
|
|
||||||
#### 需要创建的文件
|
|
||||||
|
|
||||||
```
|
|
||||||
server/modules/yx/
|
|
||||||
├── dto/
|
|
||||||
│ ├── yx_volunteer_dto.go (新建)
|
|
||||||
│ ├── yx_calculation_major_dto.go (新建)
|
|
||||||
│ └── yx_history_major_enroll_vo.go (新建)
|
|
||||||
└── vo/
|
|
||||||
├── yx_volunteer_vo.go (新建)
|
|
||||||
├── yx_calculation_major_vo.go (新建)
|
|
||||||
└── yx_school_major_vo.go (新建)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 需要修改的接口
|
|
||||||
|
|
||||||
| Controller方法 | 当前 | 改进后 |
|
|
||||||
|----------------|------|--------|
|
|
||||||
| YxVolunteerController.Create | 接收Entity | 接收CreateVolunteerRequest DTO |
|
|
||||||
| YxVolunteerController.Update | 接收Entity | 接收UpdateVolunteerRequest DTO |
|
|
||||||
| YxVolunteerController.List | 返回Entity | 返回[]YxVolunteerVO |
|
|
||||||
| YxCalculationMajorController.Create | 接收Entity | 接收CreateCalculationMajorRequest DTO |
|
|
||||||
| YxCalculationMajorController.Update | 接收Entity | 接收UpdateCalculationMajorRequest DTO |
|
|
||||||
| YxCalculationMajorController.List | 返回Entity | 返回[]YxCalculationMajorVO |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、代码示例
|
|
||||||
|
|
||||||
### 4.1 创建请求DTO
|
|
||||||
|
|
||||||
```go
|
|
||||||
// server/modules/system/dto/sys_user_dto.go
|
|
||||||
package dto
|
|
||||||
|
|
||||||
// CreateUserRequest 创建用户请求
|
|
||||||
type CreateUserRequest struct {
|
|
||||||
Username string `json:"username" binding:"required,min=3,max=20"`
|
|
||||||
Realname string `json:"realname" binding:"required"`
|
|
||||||
Password string `json:"password" binding:"required,min=6"`
|
|
||||||
Email string `json:"email" binding:"omitempty,email"`
|
|
||||||
Phone string `json:"phone" binding:"omitempty,len=11"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserRequest 更新用户请求
|
|
||||||
type UpdateUserRequest struct {
|
|
||||||
Realname string `json:"realname"`
|
|
||||||
Email string `json:"email" binding:"omitempty,email"`
|
|
||||||
Phone string `json:"phone" binding:"omitempty,len=11"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Sex *int `json:"sex"`
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 创建响应VO
|
|
||||||
|
|
||||||
```go
|
|
||||||
// server/modules/system/vo/sys_user_vo.go
|
|
||||||
package vo
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// SysUserVO 用户视图对象
|
|
||||||
type SysUserVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Realname string `json:"realname"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
Sex int `json:"sex"`
|
|
||||||
Status int `json:"status"`
|
|
||||||
CreateTime *time.Time `json:"createTime"`
|
|
||||||
// 注意:不包含 Password、Salt 等敏感字段
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 Controller 修改示例
|
|
||||||
|
|
||||||
```go
|
|
||||||
// server/modules/system/controller/sys_user_controller.go
|
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
"server/modules/system/dto"
|
|
||||||
"server/modules/system/vo"
|
|
||||||
"server/modules/system/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create 创建用户
|
|
||||||
// @Summary 创建用户
|
|
||||||
// @Tags 用户管理
|
|
||||||
// @Param request body dto.CreateUserRequest true "用户信息"
|
|
||||||
// @Success 200 {object} common.Response{data=vo.SysUserVO}
|
|
||||||
// @Router /sys-users [post]
|
|
||||||
func (ctrl *SysUserController) Create(c *gin.Context) {
|
|
||||||
var req dto.CreateUserRequest
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := ctrl.service.CreateUser(&req)
|
|
||||||
if err != nil {
|
|
||||||
common.Error(c, 500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
common.Success(c, result) // 返回VO
|
|
||||||
}
|
|
||||||
|
|
||||||
// List 获取用户列表
|
|
||||||
// @Summary 获取用户列表
|
|
||||||
// @Tags 用户管理
|
|
||||||
// @Param page query int false "页码"
|
|
||||||
// @Param size query int false "每页数量"
|
|
||||||
// @Success 200 {object} common.Response{data=[]vo.SysUserVO}
|
|
||||||
// @Router /sys-users [get]
|
|
||||||
func (ctrl *SysUserController) List(c *gin.Context) {
|
|
||||||
page := common.GetPage(c)
|
|
||||||
size := common.GetSize(c)
|
|
||||||
|
|
||||||
items, total, err := ctrl.service.ListUsers(page, size)
|
|
||||||
if err != nil {
|
|
||||||
common.Error(c, 500, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
common.SuccessPage(c, items, total, page, size) // 返回[]VO
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.4 Service 修改示例
|
|
||||||
|
|
||||||
```go
|
|
||||||
// server/modules/system/service/sys_user_service.go
|
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/modules/system/dto"
|
|
||||||
"server/modules/system/entity"
|
|
||||||
"server/modules/system/vo"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateUser 创建用户并返回VO
|
|
||||||
func (s *SysUserService) CreateUser(req *dto.CreateUserRequest) (*vo.SysUserVO, error) {
|
|
||||||
// DTO 转 Entity
|
|
||||||
entityItem := &entity.SysUser{
|
|
||||||
Username: req.Username,
|
|
||||||
Realname: req.Realname,
|
|
||||||
Password: req.Password, // 会在Create方法中加密
|
|
||||||
Email: req.Email,
|
|
||||||
Phone: req.Phone,
|
|
||||||
Avatar: req.Avatar,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存到数据库
|
|
||||||
if err := s.Create(entityItem); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entity 转 VO
|
|
||||||
return s.convertToVO(entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUsers 获取用户列表
|
|
||||||
func (s *SysUserService) ListUsers(page, size int) ([]vo.SysUserVO, int64, error) {
|
|
||||||
entities, total, err := s.List(page, size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量转换 Entity 到 VO
|
|
||||||
vos := make([]vo.SysUserVO, len(entities))
|
|
||||||
for i, item := range entities {
|
|
||||||
vos[i] = s.convertToVO(&item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vos, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertToVO Entity 转 VO(私有方法)
|
|
||||||
func (s *SysUserService) convertToVO(entity *entity.SysUser) *vo.SysUserVO {
|
|
||||||
return &vo.SysUserVO{
|
|
||||||
ID: entity.ID,
|
|
||||||
Username: entity.Username,
|
|
||||||
Realname: entity.Realname,
|
|
||||||
Avatar: entity.Avatar,
|
|
||||||
Email: entity.Email,
|
|
||||||
Phone: entity.Phone,
|
|
||||||
Sex: entity.Sex,
|
|
||||||
Status: entity.Status,
|
|
||||||
CreateTime: entity.CreateTime,
|
|
||||||
// 不包含 Password、Salt 等敏感字段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、优先级排序
|
|
||||||
|
|
||||||
| 优先级 | 模块 | 改进内容 | 原因 |
|
|
||||||
|--------|------|----------|------|
|
|
||||||
| P0 | User | UserScoreController.GetByID 返回类型明确化 | 影响API契约稳定性 |
|
|
||||||
| P0 | Yx | YxVolunteerController 接口DTO/VO改造 | 安全性风险(直接暴露Entity) |
|
|
||||||
| P1 | System | SysUserController 接口DTO/VO改造 | 规范性改进 |
|
|
||||||
| P1 | User | UserVolunteerController GetVolunteerDetail VO封装 | 代码维护性 |
|
|
||||||
| P2 | Yx | YxCalculationMajorController 接口DTO/VO改造 | 规范性改进 |
|
|
||||||
| P2 | Yx | SchoolMajorDTO 去除Entity嵌套 | 架构清晰度 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、注意事项
|
|
||||||
|
|
||||||
### 6.1 敏感字段处理
|
|
||||||
|
|
||||||
- Entity 中的 `Password`、`Salt` 等敏感字段必须在 VO 中排除
|
|
||||||
- 使用 `json:"-"` 标签确保不序列化
|
|
||||||
|
|
||||||
### 6.2 转换工具函数
|
|
||||||
|
|
||||||
建议在 Service 层实现以下方法:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// DTO -> Entity
|
|
||||||
func (s *Service) convertDtoToEntity(dto *DTO) *Entity
|
|
||||||
|
|
||||||
// Entity -> VO
|
|
||||||
func (s *Service) convertEntityToVo(entity *Entity) *VO
|
|
||||||
|
|
||||||
// []Entity -> []VO
|
|
||||||
func (s *Service) convertEntitiesToVos(entities []Entity) []VO
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6.3 渐进式改进
|
|
||||||
|
|
||||||
为避免一次性改动过大,建议:
|
|
||||||
|
|
||||||
1. 先为新增接口使用DTO/VO规范
|
|
||||||
2. 再逐步改造现有接口
|
|
||||||
3. 保持向后兼容,避免破坏现有客户端调用
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、总结
|
|
||||||
|
|
||||||
### 7.1 核心问题
|
|
||||||
|
|
||||||
1. **Controller 层直接使用 Entity**:违反了分层架构原则
|
|
||||||
2. **返回类型不明确**:使用 `interface{}` 导致API契约不清晰
|
|
||||||
3. **VO 定义缺失**:大量接口直接返回Entity
|
|
||||||
4. **DTO 定义不足**:请求参数直接使用Entity
|
|
||||||
|
|
||||||
### 7.2 改进收益
|
|
||||||
|
|
||||||
1. **安全性提升**:敏感字段不会泄露到前端
|
|
||||||
2. **API 契约清晰**:请求/响应类型明确
|
|
||||||
3. **代码可维护性**:解耦前后端数据结构
|
|
||||||
4. **符合DDD规范**:清晰的领域模型分层
|
|
||||||
|
|
||||||
### 7.3 后续行动
|
|
||||||
|
|
||||||
1. 按优先级逐步改进
|
|
||||||
2. 补充单元测试
|
|
||||||
3. 更新API文档(Swagger)
|
|
||||||
4. 团队代码规范培训
|
|
||||||
|
|
@ -1,224 +0,0 @@
|
||||||
# Entity、DTO、VO 使用改进完成报告
|
|
||||||
|
|
||||||
## 改进概述
|
|
||||||
|
|
||||||
本次改进严格按照 `entity_dto_vo_usage_improvement.md` 文档中的规范执行,对项目中的 Entity、DTO、VO 使用进行了全面规范化。
|
|
||||||
|
|
||||||
## 完成的改进任务
|
|
||||||
|
|
||||||
### P0 任务(高优先级)
|
|
||||||
|
|
||||||
#### 1. User 模块 - UserScoreController.GetByID 返回类型明确化 ✅
|
|
||||||
|
|
||||||
**问题**:Service 层的 `GetByID` 方法返回 `interface{}`,导致 API 契约不清晰。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 修改 `UserScoreService.GetByID` 方法,明确返回 `vo.UserScoreVO` 类型
|
|
||||||
- 在 Service 层实现 Entity 到 VO 的转换逻辑
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/user/service/user_score_service.go:119`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 2. Yx 模块 - YxVolunteerController 接口 DTO/VO 改造 ✅
|
|
||||||
|
|
||||||
**问题**:Controller 层直接接收和返回 Entity,存在安全风险。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 创建 `server/modules/yx/dto/yx_volunteer_dto.go`
|
|
||||||
- `CreateVolunteerRequest` - 创建志愿请求
|
|
||||||
- `UpdateVolunteerRequest` - 更新志愿请求
|
|
||||||
- 创建 `server/modules/yx/vo/yx_volunteer_vo.go`
|
|
||||||
- `YxVolunteerVO` - 志愿视图对象
|
|
||||||
- 更新 `YxVolunteerController` 的 CRUD 接口
|
|
||||||
- 在 `YxVolunteerService` 中实现 DTO/VO 转换方法
|
|
||||||
- 更新 `YxVolunteerService` 中的 `ScoreService` 接口定义,使用明确的 VO 类型
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/yx/controller/yx_volunteer_controller.go`
|
|
||||||
- `server/modules/yx/service/yx_volunteer_service.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### P1 任务(中优先级)
|
|
||||||
|
|
||||||
#### 3. System 模块 - SysUserController 接口 DTO/VO 改造 ✅
|
|
||||||
|
|
||||||
**问题**:Controller 层直接接收和返回 Entity,违反分层架构原则。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 创建 `server/modules/system/dto/sys_user_dto.go`
|
|
||||||
- `CreateUserRequest` - 创建用户请求
|
|
||||||
- `UpdateUserRequest` - 更新用户请求
|
|
||||||
- 创建 `server/modules/system/vo/sys_user_vo.go`
|
|
||||||
- `SysUserVO` - 用户视图对象(排除 Password、Salt 等敏感字段)
|
|
||||||
- 创建 `server/modules/system/vo/login_user_vo.go`
|
|
||||||
- 将 `LoginUser` 从 entity 包迁移到 vo 包
|
|
||||||
- 更新 `SysUserController` 的所有接口使用 DTO 和 VO
|
|
||||||
- 在 `SysUserService` 中实现 `CreateUser`、`UpdateUser`、`ListUsers`、`GetUserByID` 方法
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/system/controller/sys_user_controller.go`
|
|
||||||
- `server/modules/system/service/sys_user_service.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 4. User 模块 - UserVolunteerController GetVolunteerDetail VO 封装 ✅
|
|
||||||
|
|
||||||
**问题**:Controller 中使用匿名结构体定义 VO,不符合规范。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 创建 `server/modules/user/vo/volunteer_detail_vo.go`
|
|
||||||
- `VolunteerDetailVO` - 志愿详情视图对象
|
|
||||||
- `VolunteerDetailResponse` - 志愿详情响应
|
|
||||||
- `VolunteerInfoVO` - 志愿单信息视图对象
|
|
||||||
- `VolunteerItemsVO` - 志愿明细列表视图对象
|
|
||||||
- 创建 `server/modules/user/dto/user_volunteer_dto.go`
|
|
||||||
- `SaveVolunteerRequest` - 保存志愿请求
|
|
||||||
- 更新 `UserVolunteerController.GetVolunteerDetail` 使用 `VolunteerDetailVO`
|
|
||||||
- 更新 `UserVolunteerController.SaveVolunteer` 使用 `SaveVolunteerRequest`
|
|
||||||
- 在 `UserScoreService` 中实现 `GetVolunteerDetail` 和 `SaveVolunteer` 方法
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/user/controller/user_volunteer_controller.go`
|
|
||||||
- `server/modules/user/service/user_score_service.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### P2 任务(低优先级)
|
|
||||||
|
|
||||||
#### 5. Yx 模块 - YxCalculationMajorController 接口 DTO/VO 改造 ✅
|
|
||||||
|
|
||||||
**问题**:Controller 层直接接收和返回 Entity。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 创建 `server/modules/yx/dto/yx_calculation_major_dto.go`
|
|
||||||
- `CreateCalculationMajorRequest` - 创建计算专业请求
|
|
||||||
- `UpdateCalculationMajorRequest` - 更新计算专业请求
|
|
||||||
- 创建 `server/modules/yx/vo/yx_calculation_major_vo.go`
|
|
||||||
- `YxCalculationMajorVO` - 计算专业视图对象
|
|
||||||
- 更新 `YxCalculationMajorController` 的 CRUD 接口
|
|
||||||
- 在 `YxCalculationMajorService` 中实现 DTO/VO 转换方法
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/yx/controller/yx_calculation_major_controller.go`
|
|
||||||
- `server/modules/yx/service/yx_calculation_major_service.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### 6. Yx 模块 - SchoolMajorDTO 去除 Entity 嵌套 ✅
|
|
||||||
|
|
||||||
**问题**:`SchoolMajorDTO` 中嵌套了 `[]entity.YxHistoryMajorEnroll`,违反 DTO 设计原则。
|
|
||||||
|
|
||||||
**改进**:
|
|
||||||
- 创建 `server/modules/yx/vo/yx_history_major_enroll_vo.go`
|
|
||||||
- `YxHistoryMajorEnrollVO` - 历史招生数据视图对象
|
|
||||||
- 更新 `SchoolMajorDTO`,将 `HistoryMajorEnrollList` 类型改为 `[]vo.YxHistoryMajorEnrollVO`
|
|
||||||
- 更新 `YxHistoryMajorEnrollService.RecommendMajorDTOListSetHistoryInfo` 方法,实现 Entity 到 VO 的转换
|
|
||||||
|
|
||||||
**影响文件**:
|
|
||||||
- `server/modules/yx/dto/yx_school_major_dto.go`
|
|
||||||
- `server/modules/yx/service/yx_history_major_enroll_service.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 新增文件列表
|
|
||||||
|
|
||||||
### DTO 文件
|
|
||||||
1. `server/modules/system/dto/sys_user_dto.go`
|
|
||||||
2. `server/modules/user/dto/user_volunteer_dto.go`
|
|
||||||
3. `server/modules/yx/dto/yx_volunteer_dto.go`
|
|
||||||
4. `server/modules/yx/dto/yx_calculation_major_dto.go`
|
|
||||||
|
|
||||||
### VO 文件
|
|
||||||
1. `server/modules/system/vo/sys_user_vo.go`
|
|
||||||
2. `server/modules/system/vo/login_user_vo.go`
|
|
||||||
3. `server/modules/user/vo/volunteer_detail_vo.go`
|
|
||||||
4. `server/modules/yx/vo/yx_volunteer_vo.go`
|
|
||||||
5. `server/modules/yx/vo/yx_calculation_major_vo.go`
|
|
||||||
6. `server/modules/yx/vo/yx_history_major_enroll_vo.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 技术要点
|
|
||||||
|
|
||||||
### 1. 指针与值类型处理
|
|
||||||
在 Go 泛型 Service 基类中,`List` 方法返回 `[]T`(值类型数组),而 `GetByID` 返回 `*T`(指针类型)。在实现转换方法时需要注意:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// List 方法返回值类型数组
|
|
||||||
entities, total, err := s.List(page, size)
|
|
||||||
vos := make([]vo.SysUserVO, len(entities))
|
|
||||||
for i := range entities {
|
|
||||||
vos[i] = *s.convertToVO(entities[i]) // 传递值类型
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID 方法返回指针类型
|
|
||||||
entityItem, err := s.GetByID(id)
|
|
||||||
return s.convertToVO(*entityItem), nil // 解引用后传递值类型
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Create 方法的参数类型
|
|
||||||
BaseService.Create 接收 `*T`(指针类型),因此在创建实体时:
|
|
||||||
|
|
||||||
```go
|
|
||||||
entityItem := &entity.SysUser{...} // 使用指针
|
|
||||||
if err := s.Create(entityItem); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.convertToVO(*entityItem), nil // 解引用后转换
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 敏感字段处理
|
|
||||||
在 VO 中排除 Entity 中的敏感字段(如 Password、Salt),使用 `json:"-"` 标签或直接不包含该字段。
|
|
||||||
|
|
||||||
### 4. 中文字段导出问题
|
|
||||||
Go 中的字段首字母大写才能导出。将 `VolunteerItemsVO` 中的中文字段改为英文字段:
|
|
||||||
|
|
||||||
```go
|
|
||||||
type VolunteerItemsVO struct {
|
|
||||||
BatchBefore []VolunteerDetailVO `json:"batchBefore"` // 提前批
|
|
||||||
BatchUndergraduate []VolunteerDetailVO `json:"batchUndergraduate"` // 本科批
|
|
||||||
BatchCollege []VolunteerDetailVO `json:"batchCollege"` // 专科批
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 编译验证
|
|
||||||
|
|
||||||
所有改进已完成并通过编译验证:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd server
|
|
||||||
go build
|
|
||||||
```
|
|
||||||
|
|
||||||
编译成功,无错误。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 改进收益
|
|
||||||
|
|
||||||
1. **安全性提升**:敏感字段(Password、Salt)不会泄露到前端
|
|
||||||
2. **API 契约清晰**:请求/响应类型明确,不再使用 `interface{}`
|
|
||||||
3. **代码可维护性**:解耦前后端数据结构,符合 DDD 规范
|
|
||||||
4. **架构清晰度**:Controller → DTO/VO → Service → Entity → Mapper,层次分明
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 后续建议
|
|
||||||
|
|
||||||
1. 更新 Swagger API 文档,使用 swag init 重新生成
|
|
||||||
2. 补充单元测试,验证 DTO/VO 转换逻辑
|
|
||||||
3. 团队代码规范培训,确保新增接口遵循相同的规范
|
|
||||||
4. 考虑使用代码生成工具自动生成 DTO/VO 转换代码
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. 本次改进保持了向后兼容,未修改现有的接口行为
|
|
||||||
2. 所有新方法都是新增的,旧方法保留以确保现有功能不受影响
|
|
||||||
3. 建议逐步迁移其他模块,先完成 P0 和 P1 任务,再逐步完成 P2 任务
|
|
||||||
|
|
@ -5,9 +5,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/modules/system/dto"
|
"server/modules/system/entity"
|
||||||
"server/modules/system/service"
|
"server/modules/system/service"
|
||||||
"server/modules/system/vo" // 用于 Swagger 注解
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
@ -20,9 +19,6 @@ func NewSysUserController() *SysUserController {
|
||||||
return &SysUserController{service: service.NewSysUserService()}
|
return &SysUserController{service: service.NewSysUserService()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// _ 确保 vo 包被导入(用于 Swagger 注解)
|
|
||||||
var _ = vo.SysUserVO{}
|
|
||||||
|
|
||||||
func (ctrl *SysUserController) RegisterRoutes(r *gin.RouterGroup) {
|
func (ctrl *SysUserController) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
r.GET("/sys-users", ctrl.List)
|
r.GET("/sys-users", ctrl.List)
|
||||||
r.GET("/sys-users/:id", ctrl.GetByID)
|
r.GET("/sys-users/:id", ctrl.GetByID)
|
||||||
|
|
@ -37,12 +33,14 @@ func (ctrl *SysUserController) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Param page query int false "页码"
|
// @Param page query int false "页码"
|
||||||
// @Param size query int false "每页数量"
|
// @Param size query int false "每页数量"
|
||||||
// @Success 200 {object} common.Response{data=[]vo.SysUserVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /sys-users [get]
|
// @Router /sys-users [get]
|
||||||
func (ctrl *SysUserController) List(c *gin.Context) {
|
func (ctrl *SysUserController) List(c *gin.Context) {
|
||||||
|
// var user *entity.LoginUser = common.GetLoginUser(c)
|
||||||
|
// log.Printf(user.Username)
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||||
items, total, err := ctrl.service.ListUsers(page, size)
|
items, total, err := ctrl.service.List(page, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
|
|
@ -53,10 +51,10 @@ func (ctrl *SysUserController) List(c *gin.Context) {
|
||||||
// @Summary 获取单个用户
|
// @Summary 获取单个用户
|
||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Param id path string true "用户ID"
|
// @Param id path string true "用户ID"
|
||||||
// @Success 200 {object} common.Response{data=vo.SysUserVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /sys-users/{id} [get]
|
// @Router /sys-users/{id} [get]
|
||||||
func (ctrl *SysUserController) GetByID(c *gin.Context) {
|
func (ctrl *SysUserController) GetByID(c *gin.Context) {
|
||||||
item, err := ctrl.service.GetUserByID(c.Param("id"))
|
item, err := ctrl.service.GetByID(c.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 404, "未找到")
|
common.Error(c, 404, "未找到")
|
||||||
return
|
return
|
||||||
|
|
@ -66,17 +64,18 @@ func (ctrl *SysUserController) GetByID(c *gin.Context) {
|
||||||
|
|
||||||
// @Summary 创建用户
|
// @Summary 创建用户
|
||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Param request body dto.CreateUserRequest true "用户信息"
|
// @Param item body entity.SysUser true "用户信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.SysUserVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /sys-users [post]
|
// @Router /sys-users [post]
|
||||||
func (ctrl *SysUserController) Create(c *gin.Context) {
|
func (ctrl *SysUserController) Create(c *gin.Context) {
|
||||||
var req dto.CreateUserRequest
|
var item entity.SysUser
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := ctrl.service.CreateUser(&req, common.GetLoginUserID(c))
|
// 设置创建人
|
||||||
if err != nil {
|
item.CreateBy = common.GetLoginUserID(c)
|
||||||
|
if err := ctrl.service.Create(&item); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -85,18 +84,19 @@ func (ctrl *SysUserController) Create(c *gin.Context) {
|
||||||
|
|
||||||
// @Summary 更新用户
|
// @Summary 更新用户
|
||||||
// @Tags 用户管理
|
// @Tags 用户管理
|
||||||
// @Param id path string true "用户ID"
|
// @Param id path string true "用户ID"
|
||||||
// @Param request body dto.UpdateUserRequest true "用户信息"
|
// @Param item body entity.SysUser true "用户信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.SysUserVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /sys-users/{id} [put]
|
// @Router /sys-users/{id} [put]
|
||||||
func (ctrl *SysUserController) Update(c *gin.Context) {
|
func (ctrl *SysUserController) Update(c *gin.Context) {
|
||||||
var req dto.UpdateUserRequest
|
var item entity.SysUser
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := ctrl.service.UpdateUser(c.Param("id"), &req)
|
item.ID = c.Param("id")
|
||||||
if err != nil {
|
item.UpdateBy = common.GetLoginUserID(c)
|
||||||
|
if err := ctrl.service.Update(&item); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package dto
|
|
||||||
|
|
||||||
// CreateUserRequest 创建用户请求
|
|
||||||
type CreateUserRequest struct {
|
|
||||||
Username string `json:"username" binding:"required,min=3,max=20"` // 登录账号
|
|
||||||
Realname string `json:"realname" binding:"required"` // 真实姓名
|
|
||||||
Password string `json:"password" binding:"required,min=6"` // 密码
|
|
||||||
Email string `json:"email" binding:"omitempty,email"` // 电子邮件
|
|
||||||
Phone string `json:"phone" binding:"omitempty,len=11"` // 电话
|
|
||||||
Avatar string `json:"avatar"` // 头像
|
|
||||||
Sex *int `json:"sex"` // 性别(0-未知,1-男,2-女)
|
|
||||||
Birthday string `json:"birthday"` // 生日
|
|
||||||
OrgCode string `json:"orgCode"` // 机构编码
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUserRequest 更新用户请求
|
|
||||||
type UpdateUserRequest struct {
|
|
||||||
Realname string `json:"realname"` // 真实姓名
|
|
||||||
Email string `json:"email" binding:"omitempty,email"` // 电子邮件
|
|
||||||
Phone string `json:"phone" binding:"omitempty,len=11"` // 电话
|
|
||||||
Avatar string `json:"avatar"` // 头像
|
|
||||||
Sex *int `json:"sex"` // 性别(0-未知,1-男,2-女)
|
|
||||||
Birthday string `json:"birthday"` // 生日
|
|
||||||
Status *int `json:"status"` // 状态(1-正常,2-冻结)
|
|
||||||
OrgCode string `json:"orgCode"` // 机构编码
|
|
||||||
}
|
|
||||||
|
|
@ -11,10 +11,8 @@ import (
|
||||||
|
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/config"
|
"server/config"
|
||||||
"server/modules/system/dto"
|
|
||||||
"server/modules/system/entity"
|
"server/modules/system/entity"
|
||||||
"server/modules/system/mapper"
|
"server/modules/system/mapper"
|
||||||
"server/modules/system/vo"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
@ -32,119 +30,6 @@ func NewSysUserService() *SysUserService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser 创建用户并返回 VO
|
|
||||||
func (s *SysUserService) CreateUser(req *dto.CreateUserRequest, createBy string) (*vo.SysUserVO, error) {
|
|
||||||
// DTO 转 Entity
|
|
||||||
entityItem := &entity.SysUser{
|
|
||||||
Username: req.Username,
|
|
||||||
Realname: req.Realname,
|
|
||||||
Password: req.Password,
|
|
||||||
Email: req.Email,
|
|
||||||
Phone: req.Phone,
|
|
||||||
Avatar: req.Avatar,
|
|
||||||
Sex: 0, // 默认值
|
|
||||||
OrgCode: req.OrgCode,
|
|
||||||
DelFlag: 0,
|
|
||||||
Status: 1,
|
|
||||||
CreateBy: createBy,
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Sex != nil {
|
|
||||||
entityItem.Sex = *req.Sex
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Birthday != "" {
|
|
||||||
birthday, err := time.Parse("2006-01-02", req.Birthday)
|
|
||||||
if err == nil {
|
|
||||||
entityItem.Birthday = &birthday
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存到数据库
|
|
||||||
if err := s.Create(entityItem); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entity 转 VO
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateUser 更新用户并返回 VO
|
|
||||||
func (s *SysUserService) UpdateUser(id string, req *dto.UpdateUserRequest) (*vo.SysUserVO, error) {
|
|
||||||
// 获取原数据
|
|
||||||
entityItem, err := s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("用户不存在: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新字段
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
if req.Realname != "" {
|
|
||||||
updateFields["realname"] = req.Realname
|
|
||||||
}
|
|
||||||
if req.Email != "" {
|
|
||||||
updateFields["email"] = req.Email
|
|
||||||
}
|
|
||||||
if req.Phone != "" {
|
|
||||||
updateFields["phone"] = req.Phone
|
|
||||||
}
|
|
||||||
if req.Avatar != "" {
|
|
||||||
updateFields["avatar"] = req.Avatar
|
|
||||||
}
|
|
||||||
if req.Sex != nil {
|
|
||||||
updateFields["sex"] = *req.Sex
|
|
||||||
}
|
|
||||||
if req.Status != nil {
|
|
||||||
updateFields["status"] = *req.Status
|
|
||||||
}
|
|
||||||
if req.OrgCode != "" {
|
|
||||||
updateFields["org_code"] = req.OrgCode
|
|
||||||
}
|
|
||||||
if req.Birthday != "" {
|
|
||||||
birthday, err := time.Parse("2006-01-02", req.Birthday)
|
|
||||||
if err == nil {
|
|
||||||
updateFields["birthday"] = birthday
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.mapper.UpdateFields(id, updateFields); err != nil {
|
|
||||||
return nil, fmt.Errorf("更新用户失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新获取更新后的数据
|
|
||||||
entityItem, err = s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("获取更新后数据失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserByID 获取用户并返回 VO
|
|
||||||
func (s *SysUserService) GetUserByID(id string) (*vo.SysUserVO, error) {
|
|
||||||
entityItem, err := s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListUsers 获取用户列表并返回 VO 列表
|
|
||||||
func (s *SysUserService) ListUsers(page, size int) ([]vo.SysUserVO, int64, error) {
|
|
||||||
entities, total, err := s.List(page, size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量转换 Entity 到 VO
|
|
||||||
vos := make([]vo.SysUserVO, len(entities))
|
|
||||||
for i := range entities {
|
|
||||||
vos[i] = *s.convertToVO(entities[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return vos, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login 用户登录(手机号登录)
|
// Login 用户登录(手机号登录)
|
||||||
func (s *SysUserService) Login(username, password string) (*entity.LoginUser, string, error) {
|
func (s *SysUserService) Login(username, password string) (*entity.LoginUser, string, error) {
|
||||||
user, err := s.mapper.FindByPhone(username)
|
user, err := s.mapper.FindByPhone(username)
|
||||||
|
|
@ -302,22 +187,3 @@ func (s *SysUserService) Create(item *entity.SysUser) error {
|
||||||
item.CreateTime = &now
|
item.CreateTime = &now
|
||||||
return s.mapper.Create(item)
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertToVO Entity 转 VO(私有方法)
|
|
||||||
func (s *SysUserService) convertToVO(entity entity.SysUser) *vo.SysUserVO {
|
|
||||||
return &vo.SysUserVO{
|
|
||||||
ID: entity.ID,
|
|
||||||
Username: entity.Username,
|
|
||||||
Realname: entity.Realname,
|
|
||||||
Avatar: entity.Avatar,
|
|
||||||
Birthday: entity.Birthday,
|
|
||||||
Sex: entity.Sex,
|
|
||||||
Email: entity.Email,
|
|
||||||
Phone: entity.Phone,
|
|
||||||
OrgCode: entity.OrgCode,
|
|
||||||
Status: entity.Status,
|
|
||||||
CreateTime: entity.CreateTime,
|
|
||||||
UpdateTime: entity.UpdateTime,
|
|
||||||
// 不包含 Password、Salt 等敏感字段
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
// LoginUserVO 登录用户视图对象
|
|
||||||
type LoginUserVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Username string `json:"username"`
|
|
||||||
Realname string `json:"realname"`
|
|
||||||
Avatar string `json:"avatar"`
|
|
||||||
Phone string `json:"phone"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
// 注意:不包含 Token,Token 应该在单独的响应结构中
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// SysUserVO 用户视图对象
|
|
||||||
type SysUserVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Username string `json:"username"` // 登录账号
|
|
||||||
Realname string `json:"realname"` // 真实姓名
|
|
||||||
Avatar string `json:"avatar"` // 头像
|
|
||||||
Birthday *time.Time `json:"birthday"` // 生日
|
|
||||||
Sex int `json:"sex"` // 性别(0-未知,1-男,2-女)
|
|
||||||
Email string `json:"email"` // 电子邮件
|
|
||||||
Phone string `json:"phone"` // 电话
|
|
||||||
OrgCode string `json:"orgCode"` // 机构编码
|
|
||||||
Status int `json:"status"` // 状态(1-正常,2-冻结)
|
|
||||||
CreateTime *time.Time `json:"createTime"` // 创建时间
|
|
||||||
UpdateTime *time.Time `json:"updateTime"` // 更新时间
|
|
||||||
// 注意:不包含 Password、Salt 等敏感字段
|
|
||||||
}
|
|
||||||
|
|
@ -2,23 +2,15 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/modules/user/dto"
|
|
||||||
"server/modules/user/service"
|
"server/modules/user/service"
|
||||||
"server/modules/user/vo"
|
|
||||||
yxDto "server/modules/yx/dto"
|
yxDto "server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
yx_service "server/modules/yx/service"
|
yx_service "server/modules/yx/service"
|
||||||
"time" // 用于时间处理
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// _ 确保包被导入(用于类型引用)
|
|
||||||
var _ = yxDto.SchoolMajorDTO{}
|
|
||||||
var _ = entity.YxVolunteerRecord{}
|
|
||||||
var _ = time.Now()
|
|
||||||
var _ = vo.VolunteerDetailVO{}
|
|
||||||
|
|
||||||
type UserVolunteerController struct {
|
type UserVolunteerController struct {
|
||||||
userScoreService *service.UserScoreService
|
userScoreService *service.UserScoreService
|
||||||
yxVolunteerService *yx_service.YxVolunteerService
|
yxVolunteerService *yx_service.YxVolunteerService
|
||||||
|
|
@ -50,36 +42,218 @@ func (ctrl *UserVolunteerController) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
// SaveVolunteer 保存志愿明细
|
// SaveVolunteer 保存志愿明细
|
||||||
// @Summary 保存志愿明细
|
// @Summary 保存志愿明细
|
||||||
// @Tags 用户志愿
|
// @Tags 用户志愿
|
||||||
// @Param request body dto.SaveVolunteerRequest true "志愿键列表"
|
// @Param keys body []string true "Keys: schoolCode_majorCode_enrollmentCode"
|
||||||
// @Success 200 {object} common.Response
|
// @Success 200 {object} common.Response
|
||||||
// @Router /user/volunteer/save [post]
|
// @Router /user/volunteer/save [post]
|
||||||
func (ctrl *UserVolunteerController) SaveVolunteer(c *gin.Context) {
|
func (ctrl *UserVolunteerController) SaveVolunteer(c *gin.Context) {
|
||||||
var req dto.SaveVolunteerRequest
|
var keys []string
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&keys); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data deduplication
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
var uniqueKeys []string
|
||||||
|
for _, key := range keys {
|
||||||
|
if !seen[key] {
|
||||||
|
seen[key] = true
|
||||||
|
uniqueKeys = append(uniqueKeys, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys = uniqueKeys
|
||||||
|
|
||||||
loginUserId := common.GetLoginUser(c).ID
|
loginUserId := common.GetLoginUser(c).ID
|
||||||
if err := ctrl.userScoreService.SaveVolunteer(loginUserId, &req); err != nil {
|
userScoreVO, err := ctrl.userScoreService.GetActiveScoreByUserID(loginUserId)
|
||||||
common.Error(c, 500, err.Error())
|
if err != nil {
|
||||||
|
common.Error(c, 500, err.Error()) // 获取用户成绩信息失败
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userScoreVO.CalculationTableName == "" {
|
||||||
|
common.Error(c, 500, "未找到计算表名")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找当前激活的志愿表
|
||||||
|
volunteer, err := ctrl.yxVolunteerService.FindActiveByScoreId(userScoreVO.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.Error(c, 500, "查找志愿表失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if volunteer == nil || volunteer.ID == "" {
|
||||||
|
common.Error(c, 500, "请先创建志愿表")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找专业信息
|
||||||
|
majors, err := ctrl.yxCalculationMajorService.FindListByCompositeKeys(userScoreVO.CalculationTableName, keys, userScoreVO.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.Error(c, 500, "查找专业信息失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 Map 用于保持顺序
|
||||||
|
majorMap := make(map[string]entity.YxCalculationMajor)
|
||||||
|
for _, major := range majors {
|
||||||
|
k := major.SchoolCode + "_" + major.MajorCode + "_" + major.EnrollmentCode
|
||||||
|
majorMap[k] = major
|
||||||
|
}
|
||||||
|
|
||||||
|
var records []entity.YxVolunteerRecord
|
||||||
|
for i, key := range keys {
|
||||||
|
if major, ok := majorMap[key]; ok {
|
||||||
|
record := entity.YxVolunteerRecord{
|
||||||
|
VolunteerID: volunteer.ID,
|
||||||
|
SchoolCode: major.SchoolCode,
|
||||||
|
MajorCode: major.MajorCode,
|
||||||
|
EnrollmentCode: major.EnrollmentCode,
|
||||||
|
Indexs: i + 1,
|
||||||
|
CreateBy: loginUserId,
|
||||||
|
CreateTime: time.Now(),
|
||||||
|
Batch: major.Batch,
|
||||||
|
EnrollProbability: major.EnrollProbability,
|
||||||
|
StudentConvertedScore: major.StudentConvertedScore,
|
||||||
|
CalculationMajorID: major.ID,
|
||||||
|
}
|
||||||
|
records = append(records, record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先删除旧数据
|
||||||
|
if err := ctrl.yxVolunteerRecordService.DeleteByVolunteerID(volunteer.ID); err != nil {
|
||||||
|
common.Error(c, 500, "删除旧数据失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量插入新数据
|
||||||
|
if len(records) > 0 {
|
||||||
|
if err := ctrl.yxVolunteerRecordService.BatchCreate(records); err != nil {
|
||||||
|
common.Error(c, 500, "保存失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
common.Success(c, "保存成功")
|
common.Success(c, "保存成功")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVolunteerDetail 获取当前志愿单详情
|
// GetVolunteerDetail 获取当前志愿单详情
|
||||||
// @Summary 获取当前志愿单详情
|
// @Summary 获取当前志愿单详情
|
||||||
// @Tags 用户志愿
|
// @Tags 用户志愿
|
||||||
// @Success 200 {object} common.Response{data=vo.VolunteerDetailResponse}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /user/volunteer/detail [get]
|
// @Router /user/volunteer/detail [get]
|
||||||
func (ctrl *UserVolunteerController) GetVolunteerDetail(c *gin.Context) {
|
func (ctrl *UserVolunteerController) GetVolunteerDetail(c *gin.Context) {
|
||||||
loginUserId := common.GetLoginUser(c).ID
|
loginUserId := common.GetLoginUser(c).ID
|
||||||
response, err := ctrl.userScoreService.GetVolunteerDetail(loginUserId)
|
userScoreVO, err := ctrl.userScoreService.GetActiveScoreByUserID(loginUserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.Success(c, response)
|
|
||||||
|
// 查找当前激活的志愿表
|
||||||
|
volunteer, err := ctrl.yxVolunteerService.FindActiveByScoreId(userScoreVO.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.Error(c, 500, "查找志愿表失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if volunteer == nil || volunteer.ID == "" {
|
||||||
|
common.Success(c, nil) // No volunteer record found
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := ctrl.yxVolunteerRecordService.FindByVolunteerID(volunteer.ID)
|
||||||
|
if err != nil {
|
||||||
|
common.Error(c, 500, "查找志愿明细失败: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch enriched details
|
||||||
|
var enrichedMajors map[string]yxDto.SchoolMajorDTO
|
||||||
|
if len(records) > 0 && userScoreVO.CalculationTableName != "" {
|
||||||
|
keys := make([]string, 0, len(records))
|
||||||
|
for _, r := range records {
|
||||||
|
keys = append(keys, r.SchoolCode+"_"+r.MajorCode+"_"+r.EnrollmentCode)
|
||||||
|
}
|
||||||
|
majors, err := ctrl.yxCalculationMajorService.FindDtoListByCompositeKeys(userScoreVO.CalculationTableName, keys, userScoreVO.ID)
|
||||||
|
if err == nil {
|
||||||
|
enrichedMajors = make(map[string]yxDto.SchoolMajorDTO)
|
||||||
|
for _, m := range majors {
|
||||||
|
// Key by composite key as ID matches record's logic (or use ID if record stores correct ID)
|
||||||
|
// Record has CalculationMajorID, but DTO also has ID. Let's use ID if reliable, else composite.
|
||||||
|
// FindDtoListByCompositeKeys returns items where ID should match.
|
||||||
|
enrichedMajors[m.SchoolCode+"_"+m.MajorCode+"_"+m.EnrollmentCode] = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grouping
|
||||||
|
// Response structure: volunteer info + grouped items
|
||||||
|
type VolunteerDetailItem struct {
|
||||||
|
entity.YxVolunteerRecord
|
||||||
|
SchoolName string `json:"schoolName"`
|
||||||
|
MajorName string `json:"majorName"`
|
||||||
|
PlanNum int `json:"planNum"`
|
||||||
|
Tuition string `json:"tuition"`
|
||||||
|
SchoolIcon string `json:"schoolIcon"`
|
||||||
|
Province string `json:"province"`
|
||||||
|
SchoolNature string `json:"schoolNature"`
|
||||||
|
InstitutionType string `json:"institutionType"`
|
||||||
|
MajorDetail string `json:"majorDetail"` // from detail
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedItems := map[string][]VolunteerDetailItem{
|
||||||
|
"提前批": {},
|
||||||
|
"本科批": {},
|
||||||
|
"专科批": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range records {
|
||||||
|
item := VolunteerDetailItem{
|
||||||
|
YxVolunteerRecord: r,
|
||||||
|
}
|
||||||
|
key := r.SchoolCode + "_" + r.MajorCode + "_" + r.EnrollmentCode
|
||||||
|
if m, ok := enrichedMajors[key]; ok {
|
||||||
|
item.SchoolName = m.SchoolName
|
||||||
|
item.MajorName = m.MajorName
|
||||||
|
item.PlanNum = m.PlanNum
|
||||||
|
item.Tuition = m.Tuition
|
||||||
|
// DTO doesn't have icon? Check DTO definition.
|
||||||
|
// SchoolMajorDTO in step 150 sql selects school_icon. But DTO struct (step 6) might not have it.
|
||||||
|
// Checking DTO definition in step 6... It does NOT have SchoolIcon.
|
||||||
|
// I need to update DTO definition if I want SchoolIcon.
|
||||||
|
// For now, let's omit if not in DTO or update DTO.
|
||||||
|
// Wait, simple fix: update DTO struct later. For now map what matches.
|
||||||
|
item.Province = m.Province
|
||||||
|
item.SchoolNature = m.SchoolNature
|
||||||
|
item.InstitutionType = m.InstitutionType
|
||||||
|
item.MajorDetail = m.Detail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map batch
|
||||||
|
// Batch might be "本科提前批", "本科A段" etc. Need to normalize to 3 buckets?
|
||||||
|
// Or just use the batch string from record/major?
|
||||||
|
// User said "志愿明细(专科批,本科批,提前批)".
|
||||||
|
// If data has "本科A段", where does it go? "本科批"?
|
||||||
|
// I'll assume exact match or simple containment.
|
||||||
|
|
||||||
|
// If specific batches exist in data like "本科A段", "本科B段", they go to "本科批"?
|
||||||
|
// Let's use simple logic:
|
||||||
|
groupKey := ""
|
||||||
|
if r.Batch == "提前批" || r.Batch == "本科提前批" {
|
||||||
|
groupKey = "提前批"
|
||||||
|
} else if r.Batch == "高职高专" || r.Batch == "专科批" {
|
||||||
|
groupKey = "专科批"
|
||||||
|
} else {
|
||||||
|
groupKey = "本科批"
|
||||||
|
}
|
||||||
|
|
||||||
|
groupedItems[groupKey] = append(groupedItems[groupKey], item)
|
||||||
|
}
|
||||||
|
|
||||||
|
common.Success(c, map[string]interface{}{
|
||||||
|
"volunteer": volunteer,
|
||||||
|
"items": groupedItems,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateVolunteerName 编辑志愿单名称
|
// UpdateVolunteerName 编辑志愿单名称
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package dto
|
|
||||||
|
|
||||||
// SaveVolunteerRequest 保存志愿请求
|
|
||||||
type SaveVolunteerRequest struct {
|
|
||||||
Keys []string `json:"keys" binding:"required"` // Keys: schoolCode_majorCode_enrollmentCode
|
|
||||||
}
|
|
||||||
|
|
@ -8,13 +8,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/config"
|
"server/config"
|
||||||
userDto "server/modules/user/dto"
|
|
||||||
"server/modules/user/vo"
|
"server/modules/user/vo"
|
||||||
yxDto "server/modules/yx/dto"
|
"server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
"server/modules/yx/service"
|
"server/modules/yx/service"
|
||||||
"server/types"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -24,7 +22,6 @@ import (
|
||||||
type UserScoreService struct {
|
type UserScoreService struct {
|
||||||
yxUserScoreService *service.YxUserScoreService
|
yxUserScoreService *service.YxUserScoreService
|
||||||
yxVolunteerService *service.YxVolunteerService
|
yxVolunteerService *service.YxVolunteerService
|
||||||
yxVolunteerRecordService *service.YxVolunteerRecordService
|
|
||||||
yxCalculationMajorService *service.YxCalculationMajorService
|
yxCalculationMajorService *service.YxCalculationMajorService
|
||||||
mapper *mapper.YxUserScoreMapper
|
mapper *mapper.YxUserScoreMapper
|
||||||
}
|
}
|
||||||
|
|
@ -97,17 +94,9 @@ func (s *UserScoreService) GetActiveScoreByUserID(userID string) (vo.UserScoreVO
|
||||||
return s.convertEntityToVo(score), nil
|
return s.convertEntityToVo(score), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserScoreService) GetByID(id string) (vo.UserScoreVO, error) {
|
func (s *UserScoreService) GetByID(id string) (interface{}, error) {
|
||||||
var score entity.YxUserScore
|
var score entity.YxUserScore
|
||||||
err := config.DB.Model(&entity.YxUserScore{}).
|
// ... (logic remains same)
|
||||||
Where("id = ?", id).
|
|
||||||
First(&score).Error
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return vo.UserScoreVO{}, fmt.Errorf("未找到成绩记录")
|
|
||||||
}
|
|
||||||
return vo.UserScoreVO{}, fmt.Errorf("查询成绩记录失败: %w", err)
|
|
||||||
}
|
|
||||||
return s.convertEntityToVo(score), nil
|
return s.convertEntityToVo(score), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,206 +136,12 @@ func NewUserScoreService() *UserScoreService {
|
||||||
yxUserScoreService: service.NewYxUserScoreService(),
|
yxUserScoreService: service.NewYxUserScoreService(),
|
||||||
yxCalculationMajorService: service.NewYxCalculationMajorService(),
|
yxCalculationMajorService: service.NewYxCalculationMajorService(),
|
||||||
yxVolunteerService: service.NewYxVolunteerService(),
|
yxVolunteerService: service.NewYxVolunteerService(),
|
||||||
yxVolunteerRecordService: service.NewYxVolunteerRecordService(),
|
|
||||||
mapper: mapper.NewYxUserScoreMapper(),
|
mapper: mapper.NewYxUserScoreMapper(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVolunteerDetail 获取志愿详情(从 Controller 移入 Service 层)
|
|
||||||
func (s *UserScoreService) GetVolunteerDetail(userID string) (*vo.VolunteerDetailResponse, error) {
|
|
||||||
userScoreVO, err := s.GetActiveScoreByUserID(userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找当前激活的志愿表
|
|
||||||
volunteer, err := s.yxVolunteerService.FindActiveByScoreId(userScoreVO.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("查找志愿表失败: %w", err)
|
|
||||||
}
|
|
||||||
if volunteer == nil || volunteer.ID == "" {
|
|
||||||
return &vo.VolunteerDetailResponse{Volunteer: nil, Items: nil}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
records, err := s.yxVolunteerRecordService.FindByVolunteerID(volunteer.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("查找志愿明细失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取丰富详情
|
|
||||||
var enrichedMajors map[string]yxDto.SchoolMajorDTO
|
|
||||||
if len(records) > 0 && userScoreVO.CalculationTableName != "" {
|
|
||||||
keys := make([]string, 0, len(records))
|
|
||||||
for _, r := range records {
|
|
||||||
keys = append(keys, r.SchoolCode+"_"+r.MajorCode+"_"+r.EnrollmentCode)
|
|
||||||
}
|
|
||||||
majors, err := s.yxCalculationMajorService.FindDtoListByCompositeKeys(userScoreVO.CalculationTableName, keys, userScoreVO.ID)
|
|
||||||
if err == nil {
|
|
||||||
enrichedMajors = make(map[string]yxDto.SchoolMajorDTO)
|
|
||||||
for _, m := range majors {
|
|
||||||
enrichedMajors[m.SchoolCode+"_"+m.MajorCode+"_"+m.EnrollmentCode] = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分组
|
|
||||||
groupedItems := &vo.VolunteerItemsVO{
|
|
||||||
BatchBefore: []vo.VolunteerDetailVO{},
|
|
||||||
BatchUndergraduate: []vo.VolunteerDetailVO{},
|
|
||||||
BatchCollege: []vo.VolunteerDetailVO{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range records {
|
|
||||||
item := vo.VolunteerDetailVO{
|
|
||||||
ID: r.ID,
|
|
||||||
VolunteerID: r.VolunteerID,
|
|
||||||
SchoolCode: r.SchoolCode,
|
|
||||||
MajorCode: r.MajorCode,
|
|
||||||
EnrollmentCode: r.EnrollmentCode,
|
|
||||||
Indexs: r.Indexs,
|
|
||||||
Batch: r.Batch,
|
|
||||||
EnrollProbability: r.EnrollProbability,
|
|
||||||
StudentConvertedScore: r.StudentConvertedScore,
|
|
||||||
CreateBy: r.CreateBy,
|
|
||||||
CreateTime: types.NewDateTime(r.CreateTime),
|
|
||||||
CalculationMajorID: r.CalculationMajorID,
|
|
||||||
}
|
|
||||||
key := r.SchoolCode + "_" + r.MajorCode + "_" + r.EnrollmentCode
|
|
||||||
if m, ok := enrichedMajors[key]; ok {
|
|
||||||
item.SchoolName = m.SchoolName
|
|
||||||
item.MajorName = m.MajorName
|
|
||||||
item.PlanNum = m.PlanNum
|
|
||||||
item.Tuition = m.Tuition
|
|
||||||
item.Province = m.Province
|
|
||||||
item.SchoolNature = m.SchoolNature
|
|
||||||
item.InstitutionType = m.InstitutionType
|
|
||||||
item.MajorDetail = m.Detail
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分批
|
|
||||||
groupKey := ""
|
|
||||||
if r.Batch == "提前批" || r.Batch == "本科提前批" {
|
|
||||||
groupKey = "提前批"
|
|
||||||
} else if r.Batch == "高职高专" || r.Batch == "专科批" {
|
|
||||||
groupKey = "专科批"
|
|
||||||
} else {
|
|
||||||
groupKey = "本科批"
|
|
||||||
}
|
|
||||||
|
|
||||||
switch groupKey {
|
|
||||||
case "提前批":
|
|
||||||
groupedItems.BatchBefore = append(groupedItems.BatchBefore, item)
|
|
||||||
case "本科批":
|
|
||||||
groupedItems.BatchUndergraduate = append(groupedItems.BatchUndergraduate, item)
|
|
||||||
case "专科批":
|
|
||||||
groupedItems.BatchCollege = append(groupedItems.BatchCollege, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
volunteerInfo := &vo.VolunteerInfoVO{
|
|
||||||
ID: volunteer.ID,
|
|
||||||
VolunteerName: volunteer.VolunteerName,
|
|
||||||
ScoreId: volunteer.ScoreId,
|
|
||||||
CreateType: volunteer.CreateType,
|
|
||||||
State: volunteer.State,
|
|
||||||
CreateBy: volunteer.CreateBy,
|
|
||||||
CreateTime: types.NewDateTime(volunteer.CreateTime),
|
|
||||||
UpdateBy: volunteer.UpdateBy,
|
|
||||||
UpdateTime: types.NewDateTime(volunteer.UpdateTime),
|
|
||||||
}
|
|
||||||
itemMap := make(map[string][]vo.VolunteerDetailVO)
|
|
||||||
|
|
||||||
itemMap["提前批"] = groupedItems.BatchBefore
|
|
||||||
itemMap["本科批"] = groupedItems.BatchUndergraduate
|
|
||||||
itemMap["专科批"] = groupedItems.BatchCollege
|
|
||||||
|
|
||||||
return &vo.VolunteerDetailResponse{
|
|
||||||
Volunteer: volunteerInfo,
|
|
||||||
Items: itemMap,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveVolunteer 保存志愿(从 Controller 移入 Service 层)
|
|
||||||
func (s *UserScoreService) SaveVolunteer(userID string, req *userDto.SaveVolunteerRequest) error {
|
|
||||||
// 数据去重
|
|
||||||
seen := make(map[string]bool)
|
|
||||||
var uniqueKeys []string
|
|
||||||
for _, key := range req.Keys {
|
|
||||||
if !seen[key] {
|
|
||||||
seen[key] = true
|
|
||||||
uniqueKeys = append(uniqueKeys, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loginUserId := userID
|
|
||||||
userScoreVO, err := s.GetActiveScoreByUserID(loginUserId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if userScoreVO.CalculationTableName == "" {
|
|
||||||
return fmt.Errorf("未找到计算表名")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找当前激活的志愿表
|
|
||||||
volunteer, err := s.yxVolunteerService.FindActiveByScoreId(userScoreVO.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("查找志愿表失败: %w", err)
|
|
||||||
}
|
|
||||||
if volunteer == nil || volunteer.ID == "" {
|
|
||||||
return fmt.Errorf("请先创建志愿表")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找专业信息
|
|
||||||
majors, err := s.yxCalculationMajorService.FindListByCompositeKeys(userScoreVO.CalculationTableName, uniqueKeys, userScoreVO.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("查找专业信息失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建 Map 用于保持顺序
|
|
||||||
majorMap := make(map[string]entity.YxCalculationMajor)
|
|
||||||
for _, major := range majors {
|
|
||||||
k := major.SchoolCode + "_" + major.MajorCode + "_" + major.EnrollmentCode
|
|
||||||
majorMap[k] = major
|
|
||||||
}
|
|
||||||
|
|
||||||
var records []entity.YxVolunteerRecord
|
|
||||||
for i, key := range uniqueKeys {
|
|
||||||
if major, ok := majorMap[key]; ok {
|
|
||||||
record := entity.YxVolunteerRecord{
|
|
||||||
VolunteerID: volunteer.ID,
|
|
||||||
SchoolCode: major.SchoolCode,
|
|
||||||
MajorCode: major.MajorCode,
|
|
||||||
EnrollmentCode: major.EnrollmentCode,
|
|
||||||
Indexs: i + 1,
|
|
||||||
CreateBy: loginUserId,
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
Batch: major.Batch,
|
|
||||||
EnrollProbability: major.EnrollProbability,
|
|
||||||
StudentConvertedScore: major.StudentConvertedScore,
|
|
||||||
CalculationMajorID: major.ID,
|
|
||||||
}
|
|
||||||
records = append(records, record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 先删除旧数据
|
|
||||||
if err := s.yxVolunteerRecordService.DeleteByVolunteerID(volunteer.ID); err != nil {
|
|
||||||
return fmt.Errorf("删除旧数据失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量插入新数据
|
|
||||||
if len(records) > 0 {
|
|
||||||
if err := s.yxVolunteerRecordService.BatchCreate(records); err != nil {
|
|
||||||
return fmt.Errorf("保存失败: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveUserScore 保存用户成绩并返回 VO
|
// SaveUserScore 保存用户成绩并返回 VO
|
||||||
func (s *UserScoreService) SaveUserScore(req *yxDto.SaveScoreRequest) (vo.UserScoreVO, error) {
|
func (s *UserScoreService) SaveUserScore(req *dto.SaveScoreRequest) (vo.UserScoreVO, error) {
|
||||||
// 1. 业务验证
|
// 1. 业务验证
|
||||||
if err := req.Validate(); err != nil {
|
if err := req.Validate(); err != nil {
|
||||||
return vo.UserScoreVO{}, err
|
return vo.UserScoreVO{}, err
|
||||||
|
|
@ -435,7 +230,7 @@ func (s *UserScoreService) SaveUserScore(req *yxDto.SaveScoreRequest) (vo.UserSc
|
||||||
}
|
}
|
||||||
|
|
||||||
// 私有方法:DTO 转 Entity
|
// 私有方法:DTO 转 Entity
|
||||||
func (s *UserScoreService) convertDtoToEntity(req *yxDto.SaveScoreRequest) *entity.YxUserScore {
|
func (s *UserScoreService) convertDtoToEntity(req *dto.SaveScoreRequest) *entity.YxUserScore {
|
||||||
entityItem := entity.YxUserScore{
|
entityItem := entity.YxUserScore{
|
||||||
CognitioPolyclinic: req.CognitioPolyclinic,
|
CognitioPolyclinic: req.CognitioPolyclinic,
|
||||||
Subjects: strings.Join(req.SubjectList, ","),
|
Subjects: strings.Join(req.SubjectList, ","),
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
import "server/types"
|
|
||||||
|
|
||||||
// VolunteerDetailVO 志愿详情视图对象
|
|
||||||
type VolunteerDetailVO struct {
|
|
||||||
ID string `json:"id"` // 志愿记录ID
|
|
||||||
VolunteerID string `json:"volunteerId"` // 志愿单ID
|
|
||||||
SchoolCode string `json:"schoolCode"` // 院校代码
|
|
||||||
MajorCode string `json:"majorCode"` // 专业代码
|
|
||||||
EnrollmentCode string `json:"enrollmentCode"` // 招生代码
|
|
||||||
Indexs int `json:"indexs"` // 志愿序号
|
|
||||||
Batch string `json:"batch"` // 批次
|
|
||||||
EnrollProbability float64 `json:"enrollProbability"` // 录取概率
|
|
||||||
StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分
|
|
||||||
CreateBy string `json:"createBy"` // 创建人
|
|
||||||
CreateTime types.DateTime `json:"createTime"` // 创建时间
|
|
||||||
CalculationMajorID string `json:"calculationMajorId"` // 计算专业ID
|
|
||||||
// 扩展字段(来自 SchoolMajorDTO)
|
|
||||||
SchoolName string `json:"schoolName"` // 院校名称
|
|
||||||
MajorName string `json:"majorName"` // 专业名称
|
|
||||||
PlanNum int `json:"planNum"` // 计划人数
|
|
||||||
Tuition string `json:"tuition"` // 学费
|
|
||||||
SchoolIcon string `json:"schoolIcon"` // 院校图标
|
|
||||||
Province string `json:"province"` // 省份
|
|
||||||
SchoolNature string `json:"schoolNature"` // 院校性质
|
|
||||||
InstitutionType string `json:"institutionType"` // 院校类型
|
|
||||||
MajorDetail string `json:"majorDetail"` // 专业详情
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolunteerDetailResponse 志愿详情响应
|
|
||||||
type VolunteerDetailResponse struct {
|
|
||||||
Volunteer *VolunteerInfoVO `json:"volunteer"` // 志愿单信息
|
|
||||||
Items map[string][]VolunteerDetailVO `json:"items"` // 志愿明细列表 *VolunteerItemsVO
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolunteerInfoVO 志愿单信息视图对象
|
|
||||||
type VolunteerInfoVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
VolunteerName string `json:"volunteerName"`
|
|
||||||
ScoreId string `json:"scoreId"`
|
|
||||||
CreateType string `json:"createType"`
|
|
||||||
State string `json:"state"`
|
|
||||||
CreateBy string `json:"createBy"`
|
|
||||||
CreateTime types.DateTime `json:"createTime"`
|
|
||||||
UpdateBy string `json:"updateBy"`
|
|
||||||
UpdateTime types.DateTime `json:"updateTime"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolunteerItemsVO 志愿明细列表视图对象
|
|
||||||
type VolunteerItemsVO struct {
|
|
||||||
BatchBefore []VolunteerDetailVO `json:"batchBefore"` // 提前批
|
|
||||||
BatchUndergraduate []VolunteerDetailVO `json:"batchUndergraduate"` // 本科批
|
|
||||||
BatchCollege []VolunteerDetailVO `json:"batchCollege"` // 专科批
|
|
||||||
}
|
|
||||||
|
|
@ -5,9 +5,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/service"
|
"server/modules/yx/service"
|
||||||
yxVO "server/modules/yx/vo"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
@ -20,9 +19,6 @@ func NewYxCalculationMajorController() *YxCalculationMajorController {
|
||||||
return &YxCalculationMajorController{service: service.NewYxCalculationMajorService()}
|
return &YxCalculationMajorController{service: service.NewYxCalculationMajorService()}
|
||||||
}
|
}
|
||||||
|
|
||||||
// _ 确保 vo 包被导入(用于 Swagger 注解)
|
|
||||||
var _ = yxVO.YxCalculationMajorVO{}
|
|
||||||
|
|
||||||
func (ctrl *YxCalculationMajorController) RegisterRoutes(r *gin.RouterGroup) {
|
func (ctrl *YxCalculationMajorController) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
r.GET("/yx-calculation-majors", ctrl.List)
|
r.GET("/yx-calculation-majors", ctrl.List)
|
||||||
r.GET("/yx-calculation-majors/:id", ctrl.GetByID)
|
r.GET("/yx-calculation-majors/:id", ctrl.GetByID)
|
||||||
|
|
@ -38,12 +34,12 @@ func (ctrl *YxCalculationMajorController) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
// @Tags 计算专业
|
// @Tags 计算专业
|
||||||
// @Param page query int false "页码"
|
// @Param page query int false "页码"
|
||||||
// @Param size query int false "每页数量"
|
// @Param size query int false "每页数量"
|
||||||
// @Success 200 {object} common.Response{data=[]vo.YxCalculationMajorVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-calculation-majors [get]
|
// @Router /yx-calculation-majors [get]
|
||||||
func (ctrl *YxCalculationMajorController) List(c *gin.Context) {
|
func (ctrl *YxCalculationMajorController) List(c *gin.Context) {
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||||
items, total, err := ctrl.service.ListCalculationMajors(page, size)
|
items, total, err := ctrl.service.List(page, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
|
|
@ -54,10 +50,10 @@ func (ctrl *YxCalculationMajorController) List(c *gin.Context) {
|
||||||
// @Summary 获取单个计算专业
|
// @Summary 获取单个计算专业
|
||||||
// @Tags 计算专业
|
// @Tags 计算专业
|
||||||
// @Param id path string true "ID"
|
// @Param id path string true "ID"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxCalculationMajorVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-calculation-majors/{id} [get]
|
// @Router /yx-calculation-majors/{id} [get]
|
||||||
func (ctrl *YxCalculationMajorController) GetByID(c *gin.Context) {
|
func (ctrl *YxCalculationMajorController) GetByID(c *gin.Context) {
|
||||||
item, err := ctrl.service.GetCalculationMajorByID(c.Param("id"))
|
item, err := ctrl.service.GetByID(c.Param("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Error(c, 404, "未找到")
|
common.Error(c, 404, "未找到")
|
||||||
return
|
return
|
||||||
|
|
@ -67,17 +63,16 @@ func (ctrl *YxCalculationMajorController) GetByID(c *gin.Context) {
|
||||||
|
|
||||||
// @Summary 创建计算专业
|
// @Summary 创建计算专业
|
||||||
// @Tags 计算专业
|
// @Tags 计算专业
|
||||||
// @Param request body dto.CreateCalculationMajorRequest true "计算专业信息"
|
// @Param item body entity.YxCalculationMajor true "计算专业信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxCalculationMajorVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-calculation-majors [post]
|
// @Router /yx-calculation-majors [post]
|
||||||
func (ctrl *YxCalculationMajorController) Create(c *gin.Context) {
|
func (ctrl *YxCalculationMajorController) Create(c *gin.Context) {
|
||||||
var req dto.CreateCalculationMajorRequest
|
var item entity.YxCalculationMajor
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := ctrl.service.CreateCalculationMajor(&req)
|
if err := ctrl.service.Create(&item); err != nil {
|
||||||
if err != nil {
|
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -86,18 +81,18 @@ func (ctrl *YxCalculationMajorController) Create(c *gin.Context) {
|
||||||
|
|
||||||
// @Summary 更新计算专业
|
// @Summary 更新计算专业
|
||||||
// @Tags 计算专业
|
// @Tags 计算专业
|
||||||
// @Param id path string true "ID"
|
// @Param id path string true "ID"
|
||||||
// @Param request body dto.UpdateCalculationMajorRequest true "计算专业信息"
|
// @Param item body entity.YxCalculationMajor true "计算专业信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxCalculationMajorVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-calculation-majors/{id} [put]
|
// @Router /yx-calculation-majors/{id} [put]
|
||||||
func (ctrl *YxCalculationMajorController) Update(c *gin.Context) {
|
func (ctrl *YxCalculationMajorController) Update(c *gin.Context) {
|
||||||
var req dto.UpdateCalculationMajorRequest
|
var item entity.YxCalculationMajor
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
item, err := ctrl.service.UpdateCalculationMajor(c.Param("id"), &req)
|
item.ID = c.Param("id")
|
||||||
if err != nil {
|
if err := ctrl.service.Update(&item); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -138,16 +133,16 @@ func (ctrl *YxCalculationMajorController) Delete(c *gin.Context) {
|
||||||
|
|
||||||
// @Summary 批量创建计算专业
|
// @Summary 批量创建计算专业
|
||||||
// @Tags 计算专业
|
// @Tags 计算专业
|
||||||
// @Param items body []dto.CreateCalculationMajorRequest true "计算专业列表"
|
// @Param items body []entity.YxCalculationMajor true "计算专业列表"
|
||||||
// @Success 200 {object} common.Response
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-calculation-majors/batch [post]
|
// @Router /yx-calculation-majors/batch [post]
|
||||||
func (ctrl *YxCalculationMajorController) BatchCreate(c *gin.Context) {
|
func (ctrl *YxCalculationMajorController) BatchCreate(c *gin.Context) {
|
||||||
var reqs []dto.CreateCalculationMajorRequest
|
var items []entity.YxCalculationMajor
|
||||||
if err := c.ShouldBindJSON(&reqs); err != nil {
|
if err := c.ShouldBindJSON(&items); err != nil {
|
||||||
common.Error(c, 400, "参数错误")
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := ctrl.service.BatchCreateCalculationMajors(&reqs); err != nil {
|
if err := ctrl.service.BatchCreate("", items); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/common"
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/service"
|
"server/modules/yx/service"
|
||||||
"server/modules/yx/vo"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
@ -36,7 +35,7 @@ func (ctrl *YxVolunteerController) RegisterRoutes(rg *gin.RouterGroup) {
|
||||||
// @Tags 志愿
|
// @Tags 志愿
|
||||||
// @Param page query int false "页码" default(1)
|
// @Param page query int false "页码" default(1)
|
||||||
// @Param size query int false "每页数量" default(10)
|
// @Param size query int false "每页数量" default(10)
|
||||||
// @Success 200 {object} common.Response{data=[]vo.YxVolunteerVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-volunteers [get]
|
// @Router /yx-volunteers [get]
|
||||||
func (ctrl *YxVolunteerController) List(c *gin.Context) {
|
func (ctrl *YxVolunteerController) List(c *gin.Context) {
|
||||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||||
|
|
@ -46,30 +45,17 @@ func (ctrl *YxVolunteerController) List(c *gin.Context) {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 转换 Entity 到 VO
|
common.Success(c, gin.H{
|
||||||
vos := make([]vo.YxVolunteerVO, len(items))
|
"items": items,
|
||||||
for i, item := range items {
|
"total": total,
|
||||||
vos[i] = vo.YxVolunteerVO{
|
})
|
||||||
ID: item.ID,
|
|
||||||
VolunteerName: item.VolunteerName,
|
|
||||||
ScoreId: item.ScoreId,
|
|
||||||
CreateType: item.CreateType,
|
|
||||||
State: item.State,
|
|
||||||
CreateBy: item.CreateBy,
|
|
||||||
CreateTime: item.CreateTime,
|
|
||||||
UpdateBy: item.UpdateBy,
|
|
||||||
UpdateTime: item.UpdateTime,
|
|
||||||
SysOrgCode: item.SysOrgCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
common.SuccessPage(c, vos, total, page, size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 获取单个志愿
|
// Get 获取单个志愿
|
||||||
// @Summary 获取单个志愿
|
// @Summary 获取单个志愿
|
||||||
// @Tags 志愿
|
// @Tags 志愿
|
||||||
// @Param id path string true "ID"
|
// @Param id path string true "ID"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxVolunteerVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-volunteers/{id} [get]
|
// @Router /yx-volunteers/{id} [get]
|
||||||
func (ctrl *YxVolunteerController) Get(c *gin.Context) {
|
func (ctrl *YxVolunteerController) Get(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
@ -78,66 +64,53 @@ func (ctrl *YxVolunteerController) Get(c *gin.Context) {
|
||||||
common.Error(c, 404, "未找到记录")
|
common.Error(c, 404, "未找到记录")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
voItem := vo.YxVolunteerVO{
|
common.Success(c, item)
|
||||||
ID: item.ID,
|
|
||||||
VolunteerName: item.VolunteerName,
|
|
||||||
ScoreId: item.ScoreId,
|
|
||||||
CreateType: item.CreateType,
|
|
||||||
State: item.State,
|
|
||||||
CreateBy: item.CreateBy,
|
|
||||||
CreateTime: item.CreateTime,
|
|
||||||
UpdateBy: item.UpdateBy,
|
|
||||||
UpdateTime: item.UpdateTime,
|
|
||||||
SysOrgCode: item.SysOrgCode,
|
|
||||||
}
|
|
||||||
common.Success(c, voItem)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建志愿
|
// Create 创建志愿
|
||||||
// @Summary 创建志愿
|
// @Summary 创建志愿
|
||||||
// @Tags 志愿
|
// @Tags 志愿
|
||||||
// @Param request body dto.CreateVolunteerRequest true "志愿信息"
|
// @Param item body entity.YxVolunteer true "志愿信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxVolunteerVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-volunteers [post]
|
// @Router /yx-volunteers [post]
|
||||||
func (ctrl *YxVolunteerController) Create(c *gin.Context) {
|
func (ctrl *YxVolunteerController) Create(c *gin.Context) {
|
||||||
var req dto.CreateVolunteerRequest
|
var item entity.YxVolunteer
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
voItem, err := ctrl.service.CreateVolunteer(&req)
|
if err := ctrl.service.Create(&item); err != nil {
|
||||||
if err != nil {
|
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.Success(c, voItem)
|
common.Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update 更新志愿
|
// Update 更新志愿
|
||||||
// @Summary 更新志愿
|
// @Summary 更新志愿
|
||||||
// @Tags 志愿
|
// @Tags 志愿
|
||||||
// @Param id path string true "ID"
|
// @Param id path string true "ID"
|
||||||
// @Param request body dto.UpdateVolunteerRequest true "志愿信息"
|
// @Param item body entity.YxVolunteer true "志愿信息"
|
||||||
// @Success 200 {object} common.Response{data=vo.YxVolunteerVO}
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-volunteers/{id} [put]
|
// @Router /yx-volunteers/{id} [put]
|
||||||
func (ctrl *YxVolunteerController) Update(c *gin.Context) {
|
func (ctrl *YxVolunteerController) Update(c *gin.Context) {
|
||||||
var req dto.UpdateVolunteerRequest
|
var item entity.YxVolunteer
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&item); err != nil {
|
||||||
common.Error(c, 400, "参数错误: "+err.Error())
|
common.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
voItem, err := ctrl.service.UpdateVolunteer(c.Param("id"), &req)
|
item.ID = c.Param("id")
|
||||||
if err != nil {
|
if err := ctrl.service.Update(&item); err != nil {
|
||||||
common.Error(c, 500, err.Error())
|
common.Error(c, 500, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.Success(c, voItem)
|
common.Success(c, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除志愿
|
// Delete 删除志愿
|
||||||
// @Summary 删除志愿
|
// @Summary 删除志愿
|
||||||
// @Tags 志愿
|
// @Tags 志愿
|
||||||
// @Param id path string true "ID"
|
// @Param id path string true "ID"
|
||||||
// @Success 200 {object} common.Response
|
// @Success 200 {object} common.Response
|
||||||
// @Router /yx-volunteers/{id} [delete]
|
// @Router /yx-volunteers/{id} [delete]
|
||||||
func (ctrl *YxVolunteerController) Delete(c *gin.Context) {
|
func (ctrl *YxVolunteerController) Delete(c *gin.Context) {
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
package dto
|
|
||||||
|
|
||||||
// CreateCalculationMajorRequest 创建计算专业请求
|
|
||||||
type CreateCalculationMajorRequest struct {
|
|
||||||
SchoolCode string `json:"schoolCode" binding:"required"` // 院校代码
|
|
||||||
SchoolName string `json:"schoolName"` // 院校名称
|
|
||||||
MajorCode string `json:"majorCode" binding:"required"` // 专业代码
|
|
||||||
MajorName string `json:"majorName"` // 专业名称
|
|
||||||
MajorType string `json:"majorType"` // 专业类型
|
|
||||||
MajorTypeChild string `json:"majorTypeChild"` // 子专业类型
|
|
||||||
PlanNum int `json:"planNum"` // 计划人数
|
|
||||||
MainSubjects string `json:"mainSubjects"` // 主考科目
|
|
||||||
Limitation string `json:"limitation"` // 限制条件
|
|
||||||
ChineseScoreLimitation float64 `json:"chineseScoreLimitation"` // 语文分数限制
|
|
||||||
EnglishScoreLimitation float64 `json:"englishScoreLimitation"` // 英语分数限制
|
|
||||||
CulturalScoreLimitation float64 `json:"culturalScoreLimitation"` // 文化成绩限制
|
|
||||||
ProfessionalScoreLimitation float64 `json:"professionalScoreLimitation"` // 专业分数限制
|
|
||||||
EnrollmentCode string `json:"enrollmentCode"` // 招生代码
|
|
||||||
Tuition string `json:"tuition"` // 学费
|
|
||||||
Detail string `json:"detail"` // 详情
|
|
||||||
Category string `json:"category"` // 类别
|
|
||||||
Batch string `json:"batch"` // 批次
|
|
||||||
RulesEnrollProbability string `json:"rulesEnrollProbability"` // 录取规则概率
|
|
||||||
ProbabilityOperator string `json:"probabilityOperator"` // 概率操作符
|
|
||||||
Kslx string `json:"kslx"` // 考试类型
|
|
||||||
State string `json:"state"` // 状态
|
|
||||||
Province string `json:"province"` // 省份
|
|
||||||
SchoolNature string `json:"schoolNature"` // 院校性质
|
|
||||||
InstitutionType string `json:"institutionType"` // 院校类型
|
|
||||||
EnrollProbability float64 `json:"enrollProbability"` // 录取概率
|
|
||||||
StudentScore float64 `json:"studentScore"` // 学生分数
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCalculationMajorRequest 更新计算专业请求
|
|
||||||
type UpdateCalculationMajorRequest struct {
|
|
||||||
SchoolName string `json:"schoolName"` // 院校名称
|
|
||||||
MajorName string `json:"majorName"` // 专业名称
|
|
||||||
MajorType string `json:"majorType"` // 专业类型
|
|
||||||
MajorTypeChild string `json:"majorTypeChild"` // 子专业类型
|
|
||||||
PlanNum int `json:"planNum"` // 计划人数
|
|
||||||
MainSubjects string `json:"mainSubjects"` // 主考科目
|
|
||||||
Limitation string `json:"limitation"` // 限制条件
|
|
||||||
ChineseScoreLimitation float64 `json:"chineseScoreLimitation"` // 语文分数限制
|
|
||||||
EnglishScoreLimitation float64 `json:"englishScoreLimitation"` // 英语分数限制
|
|
||||||
CulturalScoreLimitation float64 `json:"culturalScoreLimitation"` // 文化成绩限制
|
|
||||||
ProfessionalScoreLimitation float64 `json:"professionalScoreLimitation"` // 专业分数限制
|
|
||||||
EnrollmentCode string `json:"enrollmentCode"` // 招生代码
|
|
||||||
Tuition string `json:"tuition"` // 学费
|
|
||||||
Detail string `json:"detail"` // 详情
|
|
||||||
Category string `json:"category"` // 类别
|
|
||||||
Batch string `json:"batch"` // 批次
|
|
||||||
RulesEnrollProbability string `json:"rulesEnrollProbability"` // 录取规则概率
|
|
||||||
ProbabilityOperator string `json:"probabilityOperator"` // 概率操作符
|
|
||||||
Kslx string `json:"kslx"` // 考试类型
|
|
||||||
State string `json:"state"` // 状态
|
|
||||||
Province string `json:"province"` // 省份
|
|
||||||
SchoolNature string `json:"schoolNature"` // 院校性质
|
|
||||||
InstitutionType string `json:"institutionType"` // 院校类型
|
|
||||||
EnrollProbability float64 `json:"enrollProbability"` // 录取概率
|
|
||||||
StudentScore float64 `json:"studentScore"` // 学生分数
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
userVO "server/modules/user/vo"
|
userVO "server/modules/user/vo"
|
||||||
"server/modules/yx/vo"
|
"server/modules/yx/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserMajorDTO struct {
|
type UserMajorDTO struct {
|
||||||
|
|
@ -73,15 +73,15 @@ type SchoolMajorDTO struct {
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
HistoryMajorEnrollMap map[string]YxHistoryMajorEnrollDTO `json:"historyMajorEnrollMap"`
|
HistoryMajorEnrollMap map[string]YxHistoryMajorEnrollDTO `json:"historyMajorEnrollMap"`
|
||||||
// 计算相关字段 (非数据库直接映射)
|
// 计算相关字段 (非数据库直接映射)
|
||||||
HistoryMajorEnrollList []vo.YxHistoryMajorEnrollVO `json:"historyMajorEnrollList"`
|
HistoryMajorEnrollList []entity.YxHistoryMajorEnroll `json:"historyMajorEnrollList"`
|
||||||
EnrollProbability float64 `json:"enrollProbability"` // 录取率
|
EnrollProbability float64 `json:"enrollProbability"` // 录取率
|
||||||
StudentScore float64 `json:"studentScore"` // 学生折合分
|
StudentScore float64 `json:"studentScore"` // 学生折合分
|
||||||
PrivateStudentScore float64 `json:"privateStudentScore"` // 学生折合分(私有)
|
PrivateStudentScore float64 `json:"privateStudentScore"` // 学生折合分(私有)
|
||||||
StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分(转换后)
|
StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分(转换后)
|
||||||
FirstLevelDiscipline string `json:"firstLevelDiscipline"` // 一级学科 (需确认来源)
|
FirstLevelDiscipline string `json:"firstLevelDiscipline"` // 一级学科 (需确认来源)
|
||||||
Province string `json:"province"` // 省份
|
Province string `json:"province"` // 省份
|
||||||
SchoolNature string `json:"schoolNature" gorm:"column:schoolNature"` // 院校性质
|
SchoolNature string `json:"schoolNature" gorm:"column:schoolNature"` // 院校性质
|
||||||
InstitutionType string `json:"institutionType" gorm:"column:institutionType"` // 院校类型
|
InstitutionType string `json:"institutionType" gorm:"column:institutionType"` // 院校类型
|
||||||
}
|
}
|
||||||
|
|
||||||
type YxHistoryMajorEnrollDTO struct {
|
type YxHistoryMajorEnrollDTO struct {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
package dto
|
|
||||||
|
|
||||||
// CreateVolunteerRequest 创建志愿请求
|
|
||||||
type CreateVolunteerRequest struct {
|
|
||||||
VolunteerName string `json:"volunteerName" binding:"required"` // 志愿单名称
|
|
||||||
ScoreId string `json:"scoreId" binding:"required"` // 关联成绩ID
|
|
||||||
CreateType string `json:"createType"` // 生成类型(1.手动生成,2.智能生成)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateVolunteerRequest 更新志愿请求
|
|
||||||
type UpdateVolunteerRequest struct {
|
|
||||||
VolunteerName string `json:"volunteerName"` // 志愿单名称
|
|
||||||
State string `json:"state"` // 志愿单状态(0-否,1-正在使用,2-历史)
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
yxDto "server/modules/yx/dto"
|
yxDto "server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
yxVO "server/modules/yx/vo"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -444,278 +443,3 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCalculationMajors 获取计算专业列表并返回 VO 列表
|
|
||||||
func (s *YxCalculationMajorService) ListCalculationMajors(page, size int) ([]yxVO.YxCalculationMajorVO, int64, error) {
|
|
||||||
entities, total, err := s.List(page, size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量转换 Entity 到 VO
|
|
||||||
vos := make([]yxVO.YxCalculationMajorVO, len(entities))
|
|
||||||
for i := range entities {
|
|
||||||
vos[i] = *s.convertToVO(entities[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return vos, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCalculationMajorByID 获取计算专业并返回 VO
|
|
||||||
func (s *YxCalculationMajorService) GetCalculationMajorByID(id string) (*yxVO.YxCalculationMajorVO, error) {
|
|
||||||
entityItem, err := s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateCalculationMajor 创建计算专业并返回 VO
|
|
||||||
func (s *YxCalculationMajorService) CreateCalculationMajor(req *yxDto.CreateCalculationMajorRequest) (*yxVO.YxCalculationMajorVO, error) {
|
|
||||||
// DTO 转 Entity - 只使用 Entity 中存在的字段
|
|
||||||
entityItem := &entity.YxCalculationMajor{
|
|
||||||
ID: uuid.New().String(),
|
|
||||||
SchoolCode: req.SchoolCode,
|
|
||||||
MajorCode: req.MajorCode,
|
|
||||||
MajorName: req.MajorName,
|
|
||||||
MajorType: req.MajorType,
|
|
||||||
MajorTypeChild: req.MajorTypeChild,
|
|
||||||
PlanNum: req.PlanNum,
|
|
||||||
MainSubjects: req.MainSubjects,
|
|
||||||
Limitation: req.Limitation,
|
|
||||||
EnrollmentCode: req.EnrollmentCode,
|
|
||||||
Tuition: req.Tuition,
|
|
||||||
Detail: req.Detail,
|
|
||||||
Category: req.Category,
|
|
||||||
Batch: req.Batch,
|
|
||||||
RulesEnrollProbability: req.RulesEnrollProbability,
|
|
||||||
ProbabilityOperator: req.ProbabilityOperator,
|
|
||||||
Kslx: req.Kslx,
|
|
||||||
State: req.State,
|
|
||||||
EnrollProbability: req.EnrollProbability,
|
|
||||||
StudentConvertedScore: req.StudentScore,
|
|
||||||
RulesEnrollProbabilitySx: req.RulesEnrollProbability,
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注意:分数限制字段存储在 OtherScoreLimitation 中,需要单独处理
|
|
||||||
var otherLimitations []string
|
|
||||||
if req.ChineseScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("语文成绩不低于%.1f分", req.ChineseScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.EnglishScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("外语成绩不低于%.1f分", req.EnglishScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.CulturalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("文化成绩不低于%.1f分", req.CulturalScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.ProfessionalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("专业成绩不低于%.1f分", req.ProfessionalScoreLimitation))
|
|
||||||
}
|
|
||||||
if len(otherLimitations) > 0 {
|
|
||||||
entityItem.OtherScoreLimitation = strings.Join(otherLimitations, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存到数据库
|
|
||||||
if err := s.Create(entityItem); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entity 转 VO
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCalculationMajor 更新计算专业并返回 VO
|
|
||||||
func (s *YxCalculationMajorService) UpdateCalculationMajor(id string, req *yxDto.UpdateCalculationMajorRequest) (*yxVO.YxCalculationMajorVO, error) {
|
|
||||||
// 获取原数据
|
|
||||||
entityItem, err := s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("计算专业不存在: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新字段 - 只使用 Entity 中存在的字段
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
if req.MajorName != "" {
|
|
||||||
updateFields["major_name"] = req.MajorName
|
|
||||||
}
|
|
||||||
if req.MajorType != "" {
|
|
||||||
updateFields["major_type"] = req.MajorType
|
|
||||||
}
|
|
||||||
if req.MajorTypeChild != "" {
|
|
||||||
updateFields["major_type_child"] = req.MajorTypeChild
|
|
||||||
}
|
|
||||||
if req.PlanNum > 0 {
|
|
||||||
updateFields["plan_num"] = req.PlanNum
|
|
||||||
}
|
|
||||||
if req.MainSubjects != "" {
|
|
||||||
updateFields["main_subjects"] = req.MainSubjects
|
|
||||||
}
|
|
||||||
if req.Limitation != "" {
|
|
||||||
updateFields["limitation"] = req.Limitation
|
|
||||||
}
|
|
||||||
if req.EnrollmentCode != "" {
|
|
||||||
updateFields["enrollment_code"] = req.EnrollmentCode
|
|
||||||
}
|
|
||||||
if req.Tuition != "" {
|
|
||||||
updateFields["tuition"] = req.Tuition
|
|
||||||
}
|
|
||||||
if req.Detail != "" {
|
|
||||||
updateFields["detail"] = req.Detail
|
|
||||||
}
|
|
||||||
if req.Category != "" {
|
|
||||||
updateFields["category"] = req.Category
|
|
||||||
}
|
|
||||||
if req.Batch != "" {
|
|
||||||
updateFields["batch"] = req.Batch
|
|
||||||
}
|
|
||||||
if req.RulesEnrollProbability != "" {
|
|
||||||
updateFields["rules_enroll_probability"] = req.RulesEnrollProbability
|
|
||||||
}
|
|
||||||
if req.ProbabilityOperator != "" {
|
|
||||||
updateFields["probability_operator"] = req.ProbabilityOperator
|
|
||||||
}
|
|
||||||
if req.Kslx != "" {
|
|
||||||
updateFields["kslx"] = req.Kslx
|
|
||||||
}
|
|
||||||
if req.State != "" {
|
|
||||||
updateFields["state"] = req.State
|
|
||||||
}
|
|
||||||
if req.EnrollProbability > 0 {
|
|
||||||
updateFields["enroll_probability"] = req.EnrollProbability
|
|
||||||
}
|
|
||||||
if req.StudentScore > 0 {
|
|
||||||
updateFields["student_converted_score"] = req.StudentScore
|
|
||||||
}
|
|
||||||
if req.RulesEnrollProbability != "" {
|
|
||||||
updateFields["rules_enroll_probability_sx"] = req.RulesEnrollProbability
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理分数限制字段
|
|
||||||
var otherLimitations []string
|
|
||||||
if req.ChineseScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("语文成绩不低于%.1f分", req.ChineseScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.EnglishScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("外语成绩不低于%.1f分", req.EnglishScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.CulturalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("文化成绩不低于%.1f分", req.CulturalScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.ProfessionalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("专业成绩不低于%.1f分", req.ProfessionalScoreLimitation))
|
|
||||||
}
|
|
||||||
if len(otherLimitations) > 0 {
|
|
||||||
updateFields["other_score_limitation"] = strings.Join(otherLimitations, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.UpdateFields(id, updateFields); err != nil {
|
|
||||||
return nil, fmt.Errorf("更新计算专业失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新获取更新后的数据
|
|
||||||
entityItem, err = s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("获取更新后数据失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchCreateCalculationMajors 批量创建计算专业
|
|
||||||
func (s *YxCalculationMajorService) BatchCreateCalculationMajors(reqs *[]yxDto.CreateCalculationMajorRequest) error {
|
|
||||||
entities := make([]entity.YxCalculationMajor, 0, len(*reqs))
|
|
||||||
now := time.Now()
|
|
||||||
for _, req := range *reqs {
|
|
||||||
// DTO 转 Entity - 只使用 Entity 中存在的字段
|
|
||||||
entityItem := entity.YxCalculationMajor{
|
|
||||||
ID: uuid.New().String(),
|
|
||||||
SchoolCode: req.SchoolCode,
|
|
||||||
MajorCode: req.MajorCode,
|
|
||||||
MajorName: req.MajorName,
|
|
||||||
MajorType: req.MajorType,
|
|
||||||
MajorTypeChild: req.MajorTypeChild,
|
|
||||||
PlanNum: req.PlanNum,
|
|
||||||
MainSubjects: req.MainSubjects,
|
|
||||||
Limitation: req.Limitation,
|
|
||||||
EnrollmentCode: req.EnrollmentCode,
|
|
||||||
Tuition: req.Tuition,
|
|
||||||
Detail: req.Detail,
|
|
||||||
Category: req.Category,
|
|
||||||
Batch: req.Batch,
|
|
||||||
RulesEnrollProbability: req.RulesEnrollProbability,
|
|
||||||
ProbabilityOperator: req.ProbabilityOperator,
|
|
||||||
Kslx: req.Kslx,
|
|
||||||
State: req.State,
|
|
||||||
EnrollProbability: req.EnrollProbability,
|
|
||||||
StudentConvertedScore: req.StudentScore,
|
|
||||||
RulesEnrollProbabilitySx: req.RulesEnrollProbability,
|
|
||||||
CreateTime: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理分数限制字段
|
|
||||||
var otherLimitations []string
|
|
||||||
if req.ChineseScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("语文成绩不低于%.1f分", req.ChineseScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.EnglishScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("外语成绩不低于%.1f分", req.EnglishScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.CulturalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("文化成绩不低于%.1f分", req.CulturalScoreLimitation))
|
|
||||||
}
|
|
||||||
if req.ProfessionalScoreLimitation > 0 {
|
|
||||||
otherLimitations = append(otherLimitations, fmt.Sprintf("专业成绩不低于%.1f分", req.ProfessionalScoreLimitation))
|
|
||||||
}
|
|
||||||
if len(otherLimitations) > 0 {
|
|
||||||
entityItem.OtherScoreLimitation = strings.Join(otherLimitations, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
entities = append(entities, entityItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.BatchCreate("", entities)
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertToVO Entity 转 VO(私有方法)
|
|
||||||
func (s *YxCalculationMajorService) convertToVO(entity entity.YxCalculationMajor) *yxVO.YxCalculationMajorVO {
|
|
||||||
vo := &yxVO.YxCalculationMajorVO{
|
|
||||||
ID: entity.ID,
|
|
||||||
SchoolCode: entity.SchoolCode,
|
|
||||||
MajorCode: entity.MajorCode,
|
|
||||||
MajorName: entity.MajorName,
|
|
||||||
MajorType: entity.MajorType,
|
|
||||||
MajorTypeChild: entity.MajorTypeChild,
|
|
||||||
PlanNum: entity.PlanNum,
|
|
||||||
MainSubjects: entity.MainSubjects,
|
|
||||||
Limitation: entity.Limitation,
|
|
||||||
EnrollmentCode: entity.EnrollmentCode,
|
|
||||||
Tuition: entity.Tuition,
|
|
||||||
Detail: entity.Detail,
|
|
||||||
Category: entity.Category,
|
|
||||||
Batch: entity.Batch,
|
|
||||||
RulesEnrollProbability: entity.RulesEnrollProbability,
|
|
||||||
ProbabilityOperator: entity.ProbabilityOperator,
|
|
||||||
Kslx: entity.Kslx,
|
|
||||||
State: entity.State,
|
|
||||||
EnrollProbability: entity.EnrollProbability,
|
|
||||||
StudentScore: entity.StudentConvertedScore,
|
|
||||||
PrivateStudentScore: entity.PrivateStudentConvertedScore,
|
|
||||||
StudentConvertedScore: entity.StudentConvertedScore,
|
|
||||||
CreateTime: entity.CreateTime.Format("2006-01-02 15:04:05"),
|
|
||||||
UpdateTime: "",
|
|
||||||
}
|
|
||||||
// 注意:SchoolName, ChineseScoreLimitation 等字段需要从关联表或其他来源获取
|
|
||||||
// 这里设置为空字符串
|
|
||||||
vo.SchoolName = ""
|
|
||||||
vo.ChineseScoreLimitation = 0
|
|
||||||
vo.EnglishScoreLimitation = 0
|
|
||||||
vo.CulturalScoreLimitation = 0
|
|
||||||
vo.ProfessionalScoreLimitation = 0
|
|
||||||
vo.Province = ""
|
|
||||||
vo.SchoolNature = ""
|
|
||||||
vo.InstitutionType = ""
|
|
||||||
vo.FirstLevelDiscipline = ""
|
|
||||||
vo.CreateBy = ""
|
|
||||||
vo.UpdateBy = ""
|
|
||||||
return vo
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
"server/modules/yx/vo"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
@ -46,23 +45,11 @@ func (s *YxHistoryMajorEnrollService) RecommendMajorDTOListSetHistoryInfo(dtoLis
|
||||||
return // Log error
|
return // Log error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group by SchoolCode + MajorName 并转换为 VO
|
// Group by SchoolCode + MajorName
|
||||||
historyMap := make(map[string][]vo.YxHistoryMajorEnrollVO)
|
historyMap := make(map[string][]entity.YxHistoryMajorEnroll)
|
||||||
for _, h := range historyList {
|
for _, h := range historyList {
|
||||||
key := h.SchoolCode + "_" + h.MajorName
|
key := h.SchoolCode + "_" + h.MajorName
|
||||||
vo := vo.YxHistoryMajorEnrollVO{
|
historyMap[key] = append(historyMap[key], h)
|
||||||
ID: h.ID,
|
|
||||||
Year: h.Year,
|
|
||||||
EnrollmentCode: h.EnrollmentCode,
|
|
||||||
EnrollmentCount: h.EnrollNum, // 使用 EnrollNum 替代
|
|
||||||
RulesEnrollProbability: h.RulesEnrollProbability,
|
|
||||||
ProbabilityOperator: h.ProbabilityOperator,
|
|
||||||
AdmissionLine: h.AdmissionLine,
|
|
||||||
ControlLine: h.ControlLine,
|
|
||||||
SchoolCode: h.SchoolCode,
|
|
||||||
MajorCode: h.MajorCode,
|
|
||||||
}
|
|
||||||
historyMap[key] = append(historyMap[key], vo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range *dtoList {
|
for i := range *dtoList {
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,15 @@ package service
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"server/common"
|
"server/common"
|
||||||
userVO "server/modules/user/vo"
|
|
||||||
"server/modules/yx/dto"
|
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
yxVO "server/modules/yx/vo"
|
"server/modules/yx/vo"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScoreService 定义成绩服务的接口,用于解耦
|
// ScoreService 定义成绩服务的接口,用于解耦
|
||||||
type ScoreService interface {
|
type ScoreService interface {
|
||||||
GetByID(id string) (userVO.UserScoreVO, error)
|
GetByID(id string) (interface{}, error)
|
||||||
GetActiveScoreByID(userID string) (entity.YxUserScore, error)
|
GetActiveScoreByID(userID string) (entity.YxUserScore, error)
|
||||||
GetActiveScoreID(userID string) (string, error)
|
GetActiveScoreID(userID string) (string, error)
|
||||||
UpdateFields(id string, fields map[string]interface{}) error
|
UpdateFields(id string, fields map[string]interface{}) error
|
||||||
|
|
@ -62,60 +60,6 @@ func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) erro
|
||||||
return s.mapper.Create(&volunteer)
|
return s.mapper.Create(&volunteer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateVolunteer 创建志愿并返回 VO
|
|
||||||
func (s *YxVolunteerService) CreateVolunteer(req *dto.CreateVolunteerRequest) (*yxVO.YxVolunteerVO, error) {
|
|
||||||
// DTO 转 Entity
|
|
||||||
entityItem := &entity.YxVolunteer{
|
|
||||||
ID: common.GenerateStringID(),
|
|
||||||
VolunteerName: req.VolunteerName,
|
|
||||||
ScoreId: req.ScoreId,
|
|
||||||
CreateType: req.CreateType,
|
|
||||||
State: "0", // 默认未激活
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
UpdateTime: time.Now(),
|
|
||||||
}
|
|
||||||
if req.CreateType == "" {
|
|
||||||
entityItem.CreateType = "1" // 默认手动生成
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.mapper.Create(entityItem); err != nil {
|
|
||||||
return nil, fmt.Errorf("创建志愿失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.convertToVO(*entityItem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateVolunteer 更新志愿并返回 VO
|
|
||||||
func (s *YxVolunteerService) UpdateVolunteer(id string, req *dto.UpdateVolunteerRequest) (*yxVO.YxVolunteerVO, error) {
|
|
||||||
// 获取原数据
|
|
||||||
entityItemPtr, err := s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("志愿不存在: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新字段
|
|
||||||
updateFields := make(map[string]interface{})
|
|
||||||
if req.VolunteerName != "" {
|
|
||||||
updateFields["volunteer_name"] = req.VolunteerName
|
|
||||||
}
|
|
||||||
if req.State != "" {
|
|
||||||
updateFields["state"] = req.State
|
|
||||||
}
|
|
||||||
updateFields["update_time"] = time.Now()
|
|
||||||
|
|
||||||
if err := s.mapper.UpdateFields(id, updateFields); err != nil {
|
|
||||||
return nil, fmt.Errorf("更新志愿失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新获取更新后的数据
|
|
||||||
entityItemPtr, err = s.GetByID(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("获取更新后数据失败: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.convertToVO(*entityItemPtr), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateName 更新志愿表名称
|
// UpdateName 更新志愿表名称
|
||||||
func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
||||||
volunteer, err := s.GetByID(id)
|
volunteer, err := s.GetByID(id)
|
||||||
|
|
@ -129,7 +73,7 @@ func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByUser 查询用户的志愿单列表(包含成绩信息)
|
// ListByUser 查询用户的志愿单列表(包含成绩信息)
|
||||||
func (s *YxVolunteerService) ListByUser(userID string, page, size int) ([]yxVO.UserVolunteerVO, int64, error) {
|
func (s *YxVolunteerService) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
||||||
return s.mapper.ListByUser(userID, page, size)
|
return s.mapper.ListByUser(userID, page, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,19 +131,3 @@ func (s *YxVolunteerService) SwitchVolunteer(id, userID string, scoreService Sco
|
||||||
}
|
}
|
||||||
return scoreService.UpdateFields(volunteer.ScoreId, map[string]interface{}{"state": "1"})
|
return scoreService.UpdateFields(volunteer.ScoreId, map[string]interface{}{"state": "1"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertToVO Entity 转 VO(私有方法)
|
|
||||||
func (s *YxVolunteerService) convertToVO(entity entity.YxVolunteer) *yxVO.YxVolunteerVO {
|
|
||||||
return &yxVO.YxVolunteerVO{
|
|
||||||
ID: entity.ID,
|
|
||||||
VolunteerName: entity.VolunteerName,
|
|
||||||
ScoreId: entity.ScoreId,
|
|
||||||
CreateType: entity.CreateType,
|
|
||||||
State: entity.State,
|
|
||||||
CreateBy: entity.CreateBy,
|
|
||||||
CreateTime: entity.CreateTime,
|
|
||||||
UpdateBy: entity.UpdateBy,
|
|
||||||
UpdateTime: entity.UpdateTime,
|
|
||||||
SysOrgCode: entity.SysOrgCode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,14 @@
|
||||||
package vo
|
package vo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/types"
|
"server/modules/yx/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UserVolunteerVO 志愿单视图对象,关联成绩信息
|
// UserVolunteerVO 志愿单视图对象,关联成绩信息
|
||||||
type UserVolunteerVO struct {
|
type UserVolunteerVO struct {
|
||||||
ID string `gorm:"column:id;primaryKey" json:"id"`
|
entity.YxVolunteer
|
||||||
VolunteerName string `gorm:"column:volunteer_name" json:"volunteerName"` // 志愿单名称
|
ProfessionalCategory string `json:"professionalCategory"` // 专业类别
|
||||||
ScoreId string `gorm:"column:score_id" json:"scoreId"` // 使用成绩id
|
Province string `json:"province"` // 省份
|
||||||
CreateType string `gorm:"column:create_type;default:1" json:"createType"` // 生成类型(1.手动生成,2.智能生成)
|
CulturalScore float64 `json:"culturalScore"` // 文化分
|
||||||
State string `gorm:"column:state;default:0" json:"state"` // 志愿单状态(0-否,1.正在使用,2-历史)
|
ProfessionalScore float64 `json:"professionalScore"` // 专业成绩分
|
||||||
UpdateTime types.DateTime `gorm:"column:update_time" json:"updateTime"` // 更新日期
|
}
|
||||||
ProfessionalCategory string `json:"professionalCategory"` // 专业类别
|
|
||||||
Province string `json:"province"` // 省份
|
|
||||||
CulturalScore float64 `json:"culturalScore"` // 文化分
|
|
||||||
ProfessionalScore float64 `json:"professionalScore"` // 专业成绩分
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
// YxCalculationMajorVO 计算专业视图对象
|
|
||||||
type YxCalculationMajorVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
SchoolCode string `json:"schoolCode"` // 院校代码
|
|
||||||
SchoolName string `json:"schoolName"` // 院校名称
|
|
||||||
MajorCode string `json:"majorCode"` // 专业代码
|
|
||||||
MajorName string `json:"majorName"` // 专业名称
|
|
||||||
MajorType string `json:"majorType"` // 专业类型
|
|
||||||
MajorTypeChild string `json:"majorTypeChild"` // 子专业类型
|
|
||||||
PlanNum int `json:"planNum"` // 计划人数
|
|
||||||
MainSubjects string `json:"mainSubjects"` // 主考科目
|
|
||||||
Limitation string `json:"limitation"` // 限制条件
|
|
||||||
ChineseScoreLimitation float64 `json:"chineseScoreLimitation"` // 语文分数限制
|
|
||||||
EnglishScoreLimitation float64 `json:"englishScoreLimitation"` // 英语分数限制
|
|
||||||
CulturalScoreLimitation float64 `json:"culturalScoreLimitation"` // 文化成绩限制
|
|
||||||
ProfessionalScoreLimitation float64 `json:"professionalScoreLimitation"` // 专业分数限制
|
|
||||||
EnrollmentCode string `json:"enrollmentCode"` // 招生代码
|
|
||||||
Tuition string `json:"tuition"` // 学费
|
|
||||||
Detail string `json:"detail"` // 详情
|
|
||||||
Category string `json:"category"` // 类别
|
|
||||||
Batch string `json:"batch"` // 批次
|
|
||||||
RulesEnrollProbability string `json:"rulesEnrollProbability"` // 录取规则概率
|
|
||||||
ProbabilityOperator string `json:"probabilityOperator"` // 概率操作符
|
|
||||||
Kslx string `json:"kslx"` // 考试类型
|
|
||||||
State string `json:"state"` // 状态
|
|
||||||
Province string `json:"province"` // 省份
|
|
||||||
SchoolNature string `json:"schoolNature"` // 院校性质
|
|
||||||
InstitutionType string `json:"institutionType"` // 院校类型
|
|
||||||
EnrollProbability float64 `json:"enrollProbability"` // 录取概率
|
|
||||||
StudentScore float64 `json:"studentScore"` // 学生分数
|
|
||||||
PrivateStudentScore float64 `json:"privateStudentScore"` // 私有学生分数
|
|
||||||
StudentConvertedScore float64 `json:"studentConvertedScore"` // 学生折合分
|
|
||||||
FirstLevelDiscipline string `json:"firstLevelDiscipline"` // 一级学科
|
|
||||||
CreateBy string `json:"createBy"` // 创建人
|
|
||||||
CreateTime string `json:"createTime"` // 创建时间
|
|
||||||
UpdateBy string `json:"updateBy"` // 更新人
|
|
||||||
UpdateTime string `json:"updateTime"` // 更新时间
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
// YxHistoryMajorEnrollVO 历年专业招生视图对象
|
|
||||||
type YxHistoryMajorEnrollVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Year string `json:"year"` // 年份
|
|
||||||
EnrollmentCode string `json:"enrollmentCode"` // 招生代码
|
|
||||||
EnrollmentCount int `json:"enrollmentCount"` // 招生人数
|
|
||||||
RulesEnrollProbability string `json:"rulesEnrollProbability"` // 录取规则概率
|
|
||||||
ProbabilityOperator string `json:"probabilityOperator"` // 概率操作符
|
|
||||||
AdmissionLine float64 `json:"admissionLine"` // 录取分数线
|
|
||||||
ControlLine float64 `json:"controlLine"` // 控制线
|
|
||||||
SchoolCode string `json:"schoolCode"` // 院校代码
|
|
||||||
MajorCode string `json:"majorCode"` // 专业代码
|
|
||||||
RulesEnrollProbabilitySx string `json:"rulesEnrollProbabilitySx"` // 录取规则概率(山东)
|
|
||||||
// 其他字段...
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package vo
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// YxVolunteerVO 志愿视图对象
|
|
||||||
type YxVolunteerVO struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
VolunteerName string `json:"volunteerName"` // 志愿单名称
|
|
||||||
ScoreId string `json:"scoreId"` // 使用成绩id
|
|
||||||
CreateType string `json:"createType"` // 生成类型(1.手动生成,2.智能生成)
|
|
||||||
State string `json:"state"` // 志愿单状态(0-否,1-正在使用,2-历史)
|
|
||||||
CreateBy string `json:"createBy"` // 创建人
|
|
||||||
CreateTime time.Time `json:"createTime"` // 创建日期
|
|
||||||
UpdateBy string `json:"updateBy"` // 更新人
|
|
||||||
UpdateTime time.Time `json:"updateTime"` // 更新日期
|
|
||||||
SysOrgCode string `json:"sysOrgCode"` // 所属部门
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DateTime 自定义时间类型,JSON序列化格式为 '2006-01-02 15:04:05'
|
|
||||||
type DateTime time.Time
|
|
||||||
|
|
||||||
// MarshalJSON 实现 json.Marshaler 接口,将时间序列化为 '2006-01-02 15:04:05' 格式
|
|
||||||
func (dt DateTime) MarshalJSON() ([]byte, error) {
|
|
||||||
t := time.Time(dt)
|
|
||||||
if t.IsZero() {
|
|
||||||
return []byte("null"), nil
|
|
||||||
}
|
|
||||||
formatted := t.Format("2006-01-02 15:04:05")
|
|
||||||
return json.Marshal(formatted)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON 实现 json.Unmarshaler 接口,解析 '2006-01-02 15:04:05' 格式的时间
|
|
||||||
func (dt *DateTime) UnmarshalJSON(data []byte) error {
|
|
||||||
if string(data) == "null" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var str string
|
|
||||||
if err := json.Unmarshal(data, &str); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t, err := time.Parse("2006-01-02 15:04:05", str)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*dt = DateTime(t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value 实现 driver.Valuer 接口,用于数据库存储
|
|
||||||
func (dt DateTime) Value() (driver.Value, error) {
|
|
||||||
t := time.Time(dt)
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan 实现 sql.Scanner 接口,用于数据库读取
|
|
||||||
func (dt *DateTime) Scan(value interface{}) error {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
t, ok := value.(time.Time)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
*dt = DateTime(t)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time 转换为标准 time.Time 类型
|
|
||||||
func (dt DateTime) Time() time.Time {
|
|
||||||
return time.Time(dt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDateTime 从 time.Time 创建 DateTime
|
|
||||||
func NewDateTime(t time.Time) DateTime {
|
|
||||||
return DateTime(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now 创建当前时间的 DateTime
|
|
||||||
func Now() DateTime {
|
|
||||||
return DateTime(time.Now())
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue