初始化代码

This commit is contained in:
zhouwentao 2025-12-17 11:24:26 +08:00
commit 3f61311678
41 changed files with 7199 additions and 0 deletions

38
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,38 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "core-comand",
"type": "go",
"request": "launch",
"mode": "auto",
//
"program": "${workspaceFolder}\\cmd\\core-command",
//
"buildFlags": "-tags=include_nats_messaging",
//
"env": {
"EDGEX_SECURITY_SECRET_STORE":"false",
// "GODEBUG":"schedtrace=100000"
},
// ["-env" , 'prod']
"args":[
"-cp=consul.http://127.0.0.1:8500", "-o=true"
]
},
{
"name": "server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/server",
"cwd": "${workspaceFolder}/server",
"env": {},
"args": []
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"kiroAgent.configureMCP": "Disabled"
}

46
Help.md Normal file
View File

@ -0,0 +1,46 @@
#### 关于 Swagger 文档更新流程:
新增接口后更新文档步骤:
在 Controller 方法上添加 Swagger 注解(@Summary、@Param、@Router 等)
在 server 目录运行 swag init 重新生成文档
重启服务器
常用 Swagger 注解说明:
```go
// @Summary 接口简介
// @Description 详细描述
// @Tags 分组标签
// @Accept json
// @Produce json
// @Param name query/path/body 类型 是否必填 "描述"
// @Success 200 {object} Response "成功描述"
// @Failure 400 {object} Response "失败描述"
// @Router /path [get/post/put/delete]
```
#### 批量修改/新增/删除
Mapper 层: | 方法 | 说明 | |------|------| | UpdateFields(id, fields) | 动态字段更新,只更新指定字段 | | BatchCreate(items, batchSize) | 批量插入,分批处理 | | BatchUpdate(items) | 批量更新 (根据主键) | | BatchUpsert(items, updateColumns) | 批量插入或更新 (存在则更新) | | BatchDelete(ids) | 批量删除 |
Service 层: 同样的方法,自动处理 UUID 生成。
使用示例:
```
// 动态字段更新 - 只更新指定字段
service.UpdateFields("xxx-id", map[string]interface{}{
"school_name": "新名称",
"plan_num": 100,
})
// 批量插入
items := []entity.SchoolMajor{{...}, {...}}
service.BatchCreate(items)
// 批量插入或更新 (存在则更新指定字段)
service.BatchUpsert(items, []string{"school_name", "plan_num"})
// 批量删除
service.BatchDelete([]string{"id1", "id2", "id3"})
```

121
Task1.md Normal file
View File

@ -0,0 +1,121 @@
#### mysql 配置
url: jdbc:mysql://81.70.191.16:3306/yitisheng?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: Db$7Hn#4Jm9Pq2!Xz
driver-class-name: com.mysql.cj.jdbc.Driver
#### mysql中的表
```sql
CREATE TABLE yitisheng.`yx_calculation_major_2025_2` (
`id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`score_id` varchar(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '成绩单id',
`school_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学校代码',
`major_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业代码',
`major_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业名称',
`enrollment_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '招生代码',
`tuition` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '学费',
`detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '备注',
`category` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '科类(文科/理科)',
`rules_enroll_probability` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式',
`batch` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '批次',
`student_old_converted_score` decimal(10,4) DEFAULT '0.0000' COMMENT '学生的未换算折合分数',
`student_converted_score` decimal(10,4) DEFAULT '0.0000' COMMENT '学生的折合分数',
`enroll_probability` decimal(10,4) DEFAULT '0.0000' COMMENT '录取率',
`probability_operator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取概率计算规则运算符',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`major_type` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业类型',
`major_type_child` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '二级专业类型',
`plan_num` int DEFAULT '0' COMMENT '计划招生人数',
`main_subjects` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主考科目',
`limitation` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '院校限制',
`other_score_limitation` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '其他分数限制',
`rules_enroll_probability_sx` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式缩写',
`kslx` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '考试类型(统考/校考)',
`private_student_converted_score` decimal(10,4) DEFAULT '0.0000' COMMENT '内部折合分数',
`private_rules_enroll_probability` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '内部录取方式',
`private_probability_operator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '内部录取方式运算符',
`state` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`) USING BTREE,
KEY `a_score_id` (`score_id`),
KEY `a_id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- yitisheng.yx_school_major definition
CREATE TABLE `yx_school_major` (
`id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`school_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学校代码',
`school_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学校名称',
`major_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业代码',
`major_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业名称',
`major_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业类型',
`major_type_child` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '二级专业类型',
`main_subjects` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主考科目',
`enrollment_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '招生代码',
`category` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '科类(文科/理科)',
`batch` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '批次',
`tuition` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '学费',
`plan_num` int DEFAULT '0' COMMENT '计划招生人数',
`detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '备注',
`semester` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学制',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新日期',
`rules_enroll_probability_sx` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式缩写',
`rules_enroll_probability` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式',
`probability_operator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取概率计算规则运算符',
`cultural_control_line` decimal(10,4) DEFAULT '0.0000' COMMENT '文化分省控线',
`special_control_line` decimal(10,4) DEFAULT '0.0000' COMMENT '专项分数线',
`check_master` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否使用主项成绩',
`limitation` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '院校限制',
`professional_score_limitation` decimal(10,4) DEFAULT '0.0000' COMMENT '专业分数限制',
`english_score_limitation` decimal(10,4) DEFAULT '0.0000' COMMENT '英语成绩限制',
`chinese_score_limitation` decimal(10,4) DEFAULT '0.0000' COMMENT '语文成绩限制',
`cultural_score_limitation` decimal(10,4) DEFAULT '0.0000' COMMENT '文化分数限制',
`kslx` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '统考' COMMENT '考试类型(统考/校考)',
`private_probability_operator` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '内部展示录取方式运算符',
`private_rules_enroll_probability` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '内部录取方式',
`state` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '使用状态',
PRIMARY KEY (`id`) USING BTREE,
KEY `s_state` (`state`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='院校专业关联表';
-- yitisheng.yx_history_major_enroll definition
CREATE TABLE `yx_history_major_enroll` (
`id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`school_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学校代码',
`school_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '学校名称',
`institution_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '院校代码',
`major_code` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业代码',
`major_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业名称',
`major_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业类型',
`enrollment_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '招生代码',
`category` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '科类',
`year` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '年份',
`enroll_num` int DEFAULT '0' COMMENT '招生人数',
`score_line_difference` decimal(10,2) DEFAULT '0.00' COMMENT '最低分数差',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建日期',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新日期',
`sys_org_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '所属部门',
`detail` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '备注',
`rules_enroll_probability` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式',
`control_line` decimal(10,4) DEFAULT '0.0000' COMMENT '省控线',
`admission_line` decimal(10,4) DEFAULT '0.0000' COMMENT '录取线',
`probability_operator` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '录取方式运算符',
`batch` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '批次',
`one_volunteer_admission_num` int DEFAULT '0' COMMENT '一志愿录取数',
`admission_num` int DEFAULT '0' COMMENT '录取人数',
`actual_pitcher_num` int DEFAULT '0' COMMENT '实际投档人数',
`check_master` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '是否使用主项成绩',
`major_type_child` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '专业类别子级',
`main_subjects` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主考科目',
`tuition` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '学费',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='历年艺术类招生录取分数表';
```
我需要分层架构实现类似Java中的Entity,Mapper,Service,Controller。

56
Task2.md Normal file
View File

@ -0,0 +1,56 @@
#### Redis
81.70.191.16:56379
密码Rd@5Wk8#Nv3Yt6$Bm
数据库1
帮我增加Redis的使用。
```sql
-- yitisheng.sys_user definition
CREATE TABLE `sys_user` (
`id` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '主键id',
`username` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '登录账号',
`realname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '真实姓名',
`password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '密码',
`salt` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 'md5密码盐',
`avatar` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '头像',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`sex` tinyint(1) DEFAULT NULL COMMENT '性别(0-默认未知,1-男,2-女)',
`email` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '电子邮件',
`phone` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '电话',
`org_code` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '登录会话的机构编码',
`status` tinyint(1) DEFAULT NULL COMMENT '性别(1-正常,2-冻结)',
`del_flag` tinyint(1) DEFAULT NULL COMMENT '删除状态(0-正常,1-已删除)',
`third_id` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '第三方登录的唯一标识',
`third_type` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '第三方类型',
`activiti_sync` tinyint(1) DEFAULT NULL COMMENT '同步工作流引擎(1-同步,0-不同步)',
`work_no` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '工号,唯一键',
`telephone` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '座机号',
`create_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`user_identity` tinyint(1) DEFAULT NULL COMMENT '身份1普通成员 2上级',
`depart_ids` varchar(1000) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '负责部门',
`client_id` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '设备ID',
`login_tenant_id` int DEFAULT NULL COMMENT '上次登录选择租户ID',
`bpm_status` varchar(2) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '流程入职离职状态',
`wx_open_id` varchar(50) DEFAULT NULL COMMENT '微信openId',
`dy_open_id` varchar(50) DEFAULT NULL COMMENT '抖音openId',
`ip` varchar(255) DEFAULT NULL COMMENT '注册时ip地址',
`show_linediff` varchar(2) DEFAULT '0' COMMENT '是否显示历年线差',
`program_type` varchar(32) DEFAULT NULL COMMENT '所属程序',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uniq_sys_user_work_no` (`work_no`) USING BTREE,
UNIQUE KEY `uniq_sys_user_username` (`username`) USING BTREE,
UNIQUE KEY `uniq_sys_user_email` (`email`) USING BTREE,
KEY `idx_su_status` (`status`) USING BTREE,
KEY `idx_su_del_flag` (`del_flag`) USING BTREE,
KEY `idx_su_del_username` (`username`,`del_flag`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='用户表';
```
增加modules/ 包将目前calculation_major、history_major_enroll、school_major的相关代码及文件名均增加数据库表前缀(yx_这种)然后将yx_模块代码移动到modules/yx/模块下。
增加sys_user表的常规代码至modules/system/。以及封装用户登录和信息的代码将用户的登录状态储存至Redis中同时代码中接口需要登录鉴权后才可以访问个别可使用类似Java的shiroFilterFactoryBean过滤必须登录鉴权才可访问在接口代码中可以获取当前用户的登录信息。

1
server/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.go linguist-language=Go

11
server/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
## ide
**/.idea
*.iml
rebel.xml
## backend
**/target
**/logs
## front
**/*.lock

174
server/README.md Normal file
View File

@ -0,0 +1,174 @@
# 艺考招生管理系统 API
基于 Go + Gin + GORM + Redis 的 RESTful API 服务。
## 技术栈
- Go 1.21+
- Gin (Web框架)
- GORM (ORM框架)
- MySQL 8.0
- Redis (会话存储/限流)
- Swaggo (API文档)
## 项目结构
```
server/
├── main.go
├── config/
│ ├── config.go # 应用配置 (日志/安全/限流)
│ ├── database.go # MySQL配置
│ └── redis.go # Redis配置
├── common/
│ ├── response.go # 统一响应
│ ├── context.go # 上下文工具
│ ├── logger.go # 日志工具
│ └── password.go # 密码加密 (PBE)
├── middleware/
│ ├── auth.go # 登录鉴权
│ ├── security.go # 安全校验 (防暴力入侵)
│ └── ratelimit.go # 接口限流
├── modules/
│ ├── system/ # 系统模块
│ └── yx/ # 艺考模块
├── logs/ # 日志目录
│ └── 2025-12-17-1.html # HTML格式日志
└── docs/ # Swagger文档
```
## 快速开始
```bash
cd server
go mod tidy
swag init
go run main.go
```
## 配置说明
修改 `config/config.go`:
```go
var AppConfig = &appConfig{
// 日志配置
Log: LogConfig{
Level: "debug", // debug/info/warn/error
Dir: "logs", // 日志目录
Console: true, // 是否输出到控制台
},
// 安全配置
Security: SecurityConfig{
Enable: true, // 是否启用
HeaderKey: "X-App-Sign", // 签名字段
SecretKey: "yts@2025#secure", // 签名密钥
},
// 限流配置
RateLimit: RateLimitConfig{
Enable: true,
Default: RateLimitRule{Interval: 2, MaxRequests: 1}, // 默认2秒1次
Rules: map[string]RateLimitRule{
"/api/auth/login": {Interval: 5, MaxRequests: 1}, // 登录5秒1次
},
},
}
```
## 功能说明
### 1. 日志系统
- 支持 debug/info/warn/error 级别
- 输出到 HTML 文件,按日期和启动次数命名
- 可配置是否同时输出到控制台
```go
common.Debug("调试信息: %s", msg)
common.Info("普通信息: %s", msg)
common.Warn("警告信息: %s", msg)
common.LogError("错误信息: %s", msg)
```
### 2. 安全校验 (防暴力入侵)
请求头需携带签名:
```
X-App-Sign: MD5(timestamp + secretKey)
X-App-Timestamp: 毫秒时间戳
```
前端签名示例 (JavaScript):
```javascript
const timestamp = Date.now().toString();
const sign = md5(timestamp + 'yts@2025#secure');
fetch('/api/xxx', {
headers: {
'X-App-Sign': sign,
'X-App-Timestamp': timestamp,
'Authorization': 'Bearer ' + token
}
});
```
### 3. 接口限流
- 基于 Redis 滑动窗口算法
- 支持按用户ID或IP限流
- 不同接口可配置不同规则
默认规则: 2秒1次
超过限制返回: `{"code": 429, "message": "操作过快,请稍后再试"}`
配置示例:
```go
Rules: map[string]RateLimitRule{
"/api/auth/login": {Interval: 5, MaxRequests: 1}, // 5秒1次
"/api/yx-school-majors": {Interval: 1, MaxRequests: 5}, // 1秒5次
}
```
## 中间件执行顺序
```
请求 -> 安全校验 -> 限流 -> 登录鉴权 -> Controller
```
## 白名单配置
```go
// 安全校验白名单
middleware.AddSecurityWhitelist("/api/public/xxx")
// 限流白名单
middleware.AddRateLimitWhitelist("/api/public/xxx")
// 登录鉴权白名单
middleware.AddWhiteList("/api/public/xxx")
```
## API 接口
### 认证
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/auth/login | 登录 |
| POST | /api/auth/logout | 登出 |
| GET | /api/auth/info | 获取当前用户 |
### 用户管理 `/api/sys-users`
### 院校专业 `/api/yx-school-majors`
### 历年招生 `/api/yx-history-enrolls`
### 计算专业 `/api/yx-calculation-majors`
## 日志文件示例
日志以 HTML 格式保存,支持浏览器直接打开查看:
```
logs/
├── 2025-12-17-1.html # 第1次启动
├── 2025-12-17-2.html # 第2次启动
└── 2025-12-18-1.html # 新的一天
```

41
server/common/context.go Normal file
View File

@ -0,0 +1,41 @@
// Package common 公共包
package common
import (
"server/modules/system/entity"
"github.com/gin-gonic/gin"
)
const ContextUserKey = "loginUser"
// GetLoginUser 从上下文获取当前登录用户
// 在Controller中使用: user := common.GetLoginUser(c)
func GetLoginUser(c *gin.Context) *entity.LoginUser {
value, exists := c.Get(ContextUserKey)
if !exists {
return nil
}
if user, ok := value.(*entity.LoginUser); ok {
return user
}
return nil
}
// GetLoginUserID 获取当前登录用户ID
func GetLoginUserID(c *gin.Context) string {
user := GetLoginUser(c)
if user != nil {
return user.ID
}
return ""
}
// GetLoginUsername 获取当前登录用户名
func GetLoginUsername(c *gin.Context) string {
user := GetLoginUser(c)
if user != nil {
return user.Username
}
return ""
}

210
server/common/logger.go Normal file
View File

@ -0,0 +1,210 @@
// Package common 日志工具
package common
import (
"fmt"
"io"
"os"
"path/filepath"
"sync"
"time"
"server/config"
)
// 日志级别
const (
LevelDebug = iota
LevelInfo
LevelWarn
LevelError
)
var levelNames = map[int]string{
LevelDebug: "DEBUG",
LevelInfo: "INFO",
LevelWarn: "WARN",
LevelError: "ERROR",
}
var levelValues = map[string]int{
"debug": LevelDebug,
"info": LevelInfo,
"warn": LevelWarn,
"error": LevelError,
}
// Logger 日志记录器
type Logger struct {
level int
file *os.File
htmlWriter *htmlLogWriter
mu sync.Mutex
console bool
}
var (
defaultLogger *Logger
startCount int
once sync.Once
)
// InitLogger 初始化日志
func InitLogger() {
once.Do(func() {
cfg := config.AppConfig.Log
level := levelValues[cfg.Level]
// 创建日志目录
if err := os.MkdirAll(cfg.Dir, 0755); err != nil {
fmt.Println("创建日志目录失败:", err)
return
}
// 计算启动次数
startCount = getStartCount(cfg.Dir)
// 创建日志文件
filename := fmt.Sprintf("%s-%d.html", time.Now().Format("2006-01-02"), startCount)
logFilepath := filepath.Join(cfg.Dir, filename)
file, err := os.OpenFile(logFilepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("创建日志文件失败:", err)
return
}
htmlWriter := newHtmlLogWriter(file)
defaultLogger = &Logger{
level: level,
file: file,
htmlWriter: htmlWriter,
console: cfg.Console,
}
fmt.Printf("日志文件: %s\n", logFilepath)
})
}
// getStartCount 获取今日启动次数
func getStartCount(dir string) int {
today := time.Now().Format("2006-01-02")
pattern := filepath.Join(dir, today+"*.html")
matches, _ := filepath.Glob(pattern)
return len(matches) + 1
}
// CloseLogger 关闭日志
func CloseLogger() {
if defaultLogger != nil && defaultLogger.file != nil {
defaultLogger.htmlWriter.writeFooter()
defaultLogger.file.Close()
}
}
func (l *Logger) log(level int, format string, args ...interface{}) {
if l == nil || level < l.level {
return
}
l.mu.Lock()
defer l.mu.Unlock()
msg := fmt.Sprintf(format, args...)
timestamp := time.Now().Format("2006-01-02 15:04:05.000")
levelName := levelNames[level]
// 写入HTML文件
l.htmlWriter.writeLog(timestamp, levelName, msg)
// 输出到控制台
if l.console {
fmt.Printf("[%s] [%s] %s\n", timestamp, levelName, msg)
}
}
// LogDebug 调试日志
func LogDebug(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelDebug, format, args...)
}
}
// LogInfo 信息日志
func LogInfo(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelInfo, format, args...)
}
}
// LogWarn 警告日志
func LogWarn(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelWarn, format, args...)
}
}
// LogError 错误日志
func LogError(format string, args ...interface{}) {
if defaultLogger != nil {
defaultLogger.log(LevelError, format, args...)
}
}
// 简短别名
var (
Debug = LogDebug
Info = LogInfo
Warn = LogWarn
)
// htmlLogWriter HTML日志写入器
type htmlLogWriter struct {
writer io.Writer
initialized bool
}
func newHtmlLogWriter(w io.Writer) *htmlLogWriter {
hw := &htmlLogWriter{writer: w}
hw.writeHeader()
return hw
}
func (h *htmlLogWriter) writeHeader() {
html := `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>应用日志</title>
<style>
body { font-family: Consolas, monospace; background: #1e1e1e; color: #d4d4d4; padding: 20px; }
.log { margin: 2px 0; padding: 4px 8px; border-radius: 3px; }
.DEBUG { background: #2d2d2d; color: #9cdcfe; }
.INFO { background: #1e3a1e; color: #4ec9b0; }
.WARN { background: #3a3a1e; color: #dcdcaa; }
.ERROR { background: #3a1e1e; color: #f14c4c; }
.time { color: #808080; }
.level { font-weight: bold; width: 60px; display: inline-block; }
</style>
</head>
<body>
<h2>应用日志 - ` + time.Now().Format("2006-01-02") + `</h2>
<div id="logs">
`
h.writer.Write([]byte(html))
h.initialized = true
}
func (h *htmlLogWriter) writeLog(timestamp, level, msg string) {
html := fmt.Sprintf(`<div class="log %s"><span class="time">[%s]</span> <span class="level">[%s]</span> %s</div>
`, level, timestamp, level, msg)
h.writer.Write([]byte(html))
}
func (h *htmlLogWriter) writeFooter() {
html := `</div>
</body>
</html>`
h.writer.Write([]byte(html))
}

190
server/common/password.go Normal file
View File

@ -0,0 +1,190 @@
package common
import (
"bytes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
)
// PasswordUtil 对应 Java 类的常量
const (
// DefaultSalt 对应 Java 中的 SALT = "63293188"
DefaultSalt = "63293188"
// IterationCount 对应 Java 中的 ITERATIONCOUNT = 1000
IterationCount = 1000
)
// GetSalt 生成 8 字节的随机盐
func GetSalt() ([]byte, error) {
salt := make([]byte, 8)
_, err := rand.Read(salt)
if err != nil {
return nil, err
}
return salt, nil
}
// GetStaticSalt 获取静态盐
func GetStaticSalt() []byte {
return []byte(DefaultSalt)
}
// Encrypt 加密
// plaintext: 明文
// password: 密码
// salt: 盐 (传入 string, 对应 Java 的 salt 参数)
func Encrypt(plaintext, password, salt string) (string, error) {
// 1. 生成 Key 和 IV
key, iv := deriveKeyAndIV(password, salt, IterationCount)
// 2. 创建 DES Cipher
block, err := des.NewCipher(key)
if err != nil {
return "", err
}
// 3. 处理数据 (UTF-8 转 bytes 并填充)
data := []byte(plaintext)
data = pkcs5Padding(data, block.BlockSize())
// 4. 加密 (CBC 模式)
blockMode := cipher.NewCBCEncrypter(block, iv)
crypted := make([]byte, len(data))
blockMode.CryptBlocks(crypted, data)
// 5. 转十六进制字符串 (对应 Java 的 bytesToHexString)
// Java 的 Integer.toHexString 产生的是小写,这里保持一致,
// 但通常十六进制可以互通。
return hex.EncodeToString(crypted), nil
}
// Decrypt 解密
// ciphertext: 密文 (Hex 字符串)
// password: 密码
// salt: 盐
func Decrypt(ciphertext, password, salt string) (string, error) {
// 1. 生成 Key 和 IV
key, iv := deriveKeyAndIV(password, salt, IterationCount)
// 2. Hex 字符串转 bytes
decodedBytes, err := hex.DecodeString(ciphertext)
if err != nil {
return "", err
}
// 3. 创建 DES Cipher
block, err := des.NewCipher(key)
if err != nil {
return "", err
}
// 4. 解密 (CBC 模式)
blockMode := cipher.NewCBCDecrypter(block, iv)
// 密文长度检查
if len(decodedBytes)%block.BlockSize() != 0 {
return "", errors.New("ciphertext is not a multiple of the block size")
}
origData := make([]byte, len(decodedBytes))
blockMode.CryptBlocks(origData, decodedBytes)
// 5. 去除填充
origData, err = pkcs5UnPadding(origData)
if err != nil {
return "", err
}
return string(origData), nil
}
// deriveKeyAndIV 模拟 Java PBEWithMD5AndDES 的密钥派生逻辑
// 逻辑MD5(password || salt) -> 迭代 -> 结果前8字节是Key后8字节是IV
func deriveKeyAndIV(password, salt string, iterations int) ([]byte, []byte) {
// Java PBEKeySpec 处理 password 为 char[],通常转 byte 时依赖编码,
// 此处假设使用 UTF-8 兼容Java 默认行为通常如此,或者 ASCII
passBytes := []byte(password)
saltBytes := []byte(salt)
// 第一次迭代: Hash(pass + salt)
hash := md5.New()
hash.Write(passBytes)
hash.Write(saltBytes)
derived := hash.Sum(nil)
// 后续迭代: Hash(prev_hash)
for i := 1; i < iterations; i++ {
hash.Reset()
hash.Write(derived)
derived = hash.Sum(nil)
}
// MD5 结果是 16 字节
// DES Key 需要 8 字节IV 需要 8 字节
// 正好平分
key := derived[:8]
iv := derived[8:]
return key, iv
}
// pkcs5Padding 填充
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
// pkcs5UnPadding 去除填充
func pkcs5UnPadding(origData []byte) ([]byte, error) {
length := len(origData)
if length == 0 {
return nil, errors.New("decryption error: output length is zero")
}
unpadding := int(origData[length-1])
if length < unpadding {
return nil, errors.New("decryption error: invalid padding")
}
return origData[:(length - unpadding)], nil
}
// --- 测试主函数 ---
func main() {
// 测试数据
plaintext := "admin"
password := "Wang5322570"
//salt := DefaultSalt
salt := "RCGTeGiH"
fmt.Println("原文:", plaintext)
fmt.Println("密码:", password)
fmt.Println("盐值:", salt)
// 加密
encrypted, err := Encrypt(plaintext, password, salt)
if err != nil {
fmt.Println("加密失败:", err)
return
}
// Java代码可能输出小写或手动处理这里使用 hex.EncodeToString (小写)
// 如果需要大写可以使用 strings.ToUpper(encrypted)
fmt.Println("加密密文 (Hex):", encrypted)
// 解密
decrypted, err := Decrypt(encrypted, password, salt)
if err != nil {
fmt.Println("解密失败:", err)
return
}
fmt.Println("解密原文:", decrypted)
// 验证一致性
if plaintext == decrypted {
fmt.Println(">> 测试通过Go版本与逻辑一致。")
} else {
fmt.Println(">> 测试失败!")
}
}

35
server/common/response.go Normal file
View File

@ -0,0 +1,35 @@
// Package common 公共包
package common
import "github.com/gin-gonic/gin"
// Response 统一响应结构
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// PageResponse 分页响应
type PageResponse struct {
List interface{} `json:"list"`
Total int64 `json:"total"`
Page int `json:"page"`
Size int `json:"size"`
}
func Success(c *gin.Context, data interface{}) {
c.JSON(200, Response{Code: 200, Message: "success", Data: data})
}
func SuccessPage(c *gin.Context, list interface{}, total int64, page, size int) {
c.JSON(200, Response{
Code: 200,
Message: "success",
Data: PageResponse{List: list, Total: total, Page: page, Size: size},
})
}
func Error(c *gin.Context, code int, message string) {
c.JSON(code, Response{Code: code, Message: message, Data: nil})
}

60
server/config/config.go Normal file
View File

@ -0,0 +1,60 @@
// Package config 应用配置
package config
// AppConfig 应用配置
var AppConfig = &appConfig{
// 日志配置
Log: LogConfig{
Level: "debug", // debug/info/warn/error
Dir: "logs", // 日志目录
Console: true, // 是否同时输出到控制台
},
// 安全配置
Security: SecurityConfig{
Enable: true, // 是否启用安全校验
HeaderKey: "X-App-Sign", // 请求头校验字段
SecretKey: "yts@2025#secure", // 签名密钥
},
// 限流配置
RateLimit: RateLimitConfig{
Enable: true,
Default: RateLimitRule{Interval: 2, MaxRequests: 3}, // 默认2秒1次
Rules: map[string]RateLimitRule{
"/api/auth/login": {Interval: 5, MaxRequests: 1}, // 登录5秒1次
"/api/yx-school-majors": {Interval: 1, MaxRequests: 5}, // 查询1秒5次
},
},
}
type appConfig struct {
Log LogConfig
Security SecurityConfig
RateLimit RateLimitConfig
}
// LogConfig 日志配置
type LogConfig struct {
Level string // 日志级别
Dir string // 日志目录
Console bool // 是否输出到控制台
}
// SecurityConfig 安全配置
type SecurityConfig struct {
Enable bool // 是否启用
HeaderKey string // 请求头字段名
SecretKey string // 签名密钥
}
// RateLimitConfig 限流配置
type RateLimitConfig struct {
Enable bool // 是否启用
Default RateLimitRule // 默认规则
Rules map[string]RateLimitRule // 特定路径规则
}
// RateLimitRule 限流规则
type RateLimitRule struct {
Interval int // 时间间隔(秒)
MaxRequests int // 最大请求次数
}

50
server/config/database.go Normal file
View File

@ -0,0 +1,50 @@
// Package config 配置包,负责应用程序的配置管理
package config
import (
"fmt"
"log"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// DB 全局数据库连接实例
var DB *gorm.DB
// InitDB 初始化数据库连接
func InitDB() {
dsn := "root:Db$7Hn#4Jm9Pq2!Xz@tcp(81.70.191.16:3306)/yitisheng?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("数据库连接失败:", err)
}
// 获取底层 sql.DB 以配置连接池
sqlDB, err := DB.DB()
if err != nil {
log.Fatal("获取数据库实例失败:", err)
}
// 连接池配置
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间
fmt.Println("数据库连接成功")
}
// CloseDB 关闭数据库连接
// 在程序退出时调用,释放所有连接
func CloseDB() {
if DB != nil {
sqlDB, err := DB.DB()
if err == nil {
sqlDB.Close()
fmt.Println("数据库连接已关闭")
}
}
}

42
server/config/redis.go Normal file
View File

@ -0,0 +1,42 @@
// Package config Redis配置
package config
import (
"context"
"fmt"
"log"
"time"
"github.com/redis/go-redis/v9"
)
// RDB 全局Redis客户端
var RDB *redis.Client
// InitRedis 初始化Redis连接
func InitRedis() {
RDB = redis.NewClient(&redis.Options{
Addr: "81.70.191.16:56379",
Password: "Rd@5Wk8#Nv3Yt6$Bm",
DB: 1,
PoolSize: 10, // 连接池大小
})
// 测试连接
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err := RDB.Ping(ctx).Result()
if err != nil {
log.Fatal("Redis连接失败:", err)
}
fmt.Println("Redis连接成功")
}
// CloseRedis 关闭Redis连接
func CloseRedis() {
if RDB != nil {
RDB.Close()
fmt.Println("Redis连接已关闭")
}
}

1448
server/docs/docs.go Normal file

File diff suppressed because it is too large Load Diff

1424
server/docs/swagger.json Normal file

File diff suppressed because it is too large Load Diff

938
server/docs/swagger.yaml Normal file
View File

@ -0,0 +1,938 @@
basePath: /api
definitions:
common.Response:
properties:
code:
type: integer
data: {}
message:
type: string
type: object
controller.LoginRequest:
properties:
password:
type: string
username:
type: string
required:
- password
- username
type: object
controller.UpdatePasswordRequest:
properties:
newPassword:
type: string
oldPassword:
type: string
required:
- newPassword
- oldPassword
type: object
entity.SysUser:
properties:
activitiSync:
description: 同步工作流引擎
type: integer
avatar:
description: 头像
type: string
birthday:
description: 生日
type: string
bpmStatus:
description: 流程状态
type: string
clientId:
description: 设备ID
type: string
createBy:
description: 创建人
type: string
createTime:
description: 创建时间
type: string
delFlag:
description: 删除状态(0-正常,1-已删除)
type: integer
departIds:
description: 负责部门
type: string
dyOpenId:
description: 抖音openId
type: string
email:
description: 电子邮件
type: string
id:
type: string
ip:
description: 注册时ip
type: string
loginTenantId:
description: 上次登录租户ID
type: integer
orgCode:
description: 机构编码
type: string
phone:
description: 电话
type: string
programType:
description: 所属程序
type: string
realname:
description: 真实姓名
type: string
sex:
description: 性别(0-未知,1-男,2-女)
type: integer
showLinediff:
description: 是否显示历年线差
type: string
status:
description: 状态(1-正常,2-冻结)
type: integer
telephone:
description: 座机号
type: string
thirdId:
description: 第三方登录唯一标识
type: string
thirdType:
description: 第三方类型
type: string
updateBy:
description: 更新人
type: string
updateTime:
description: 更新时间
type: string
userIdentity:
description: 身份(1普通成员 2上级)
type: integer
username:
description: 登录账号
type: string
workNo:
description: 工号
type: string
wxOpenId:
description: 微信openId
type: string
type: object
entity.YxCalculationMajor:
properties:
batch:
type: string
category:
type: string
createTime:
type: string
detail:
type: string
enrollProbability:
type: number
enrollmentCode:
type: string
id:
type: string
kslx:
type: string
limitation:
type: string
mainSubjects:
type: string
majorCode:
type: string
majorName:
type: string
majorType:
type: string
majorTypeChild:
type: string
otherScoreLimitation:
type: string
planNum:
type: integer
privateProbabilityOperator:
type: string
privateRulesEnrollProbability:
type: string
privateStudentConvertedScore:
type: number
probabilityOperator:
type: string
rulesEnrollProbability:
type: string
rulesEnrollProbabilitySx:
type: string
schoolCode:
type: string
scoreId:
type: string
state:
type: string
studentConvertedScore:
type: number
studentOldConvertedScore:
type: number
tuition:
type: string
type: object
entity.YxHistoryMajorEnroll:
properties:
actualPitcherNum:
type: integer
admissionLine:
type: number
admissionNum:
type: integer
batch:
type: string
category:
type: string
checkMaster:
type: string
controlLine:
type: number
createBy:
type: string
createTime:
type: string
detail:
type: string
enrollNum:
type: integer
enrollmentCode:
type: string
id:
type: string
institutionCode:
type: string
mainSubjects:
type: string
majorCode:
type: string
majorName:
type: string
majorType:
type: string
majorTypeChild:
type: string
oneVolunteerAdmissionNum:
type: integer
probabilityOperator:
type: string
rulesEnrollProbability:
type: string
schoolCode:
type: string
schoolName:
type: string
scoreLineDifference:
type: number
sysOrgCode:
type: string
tuition:
type: string
updateBy:
type: string
updateTime:
type: string
year:
type: string
type: object
entity.YxSchoolMajor:
properties:
batch:
type: string
category:
type: string
checkMaster:
type: string
chineseScoreLimitation:
type: number
createBy:
type: string
createTime:
type: string
culturalControlLine:
type: number
culturalScoreLimitation:
type: number
detail:
type: string
englishScoreLimitation:
type: number
enrollmentCode:
type: string
id:
type: string
kslx:
type: string
limitation:
type: string
mainSubjects:
type: string
majorCode:
type: string
majorName:
type: string
majorType:
type: string
majorTypeChild:
type: string
planNum:
type: integer
privateProbabilityOperator:
type: string
privateRulesEnrollProbability:
type: string
probabilityOperator:
type: string
professionalScoreLimitation:
type: number
rulesEnrollProbability:
type: string
rulesEnrollProbabilitySx:
type: string
schoolCode:
type: string
schoolName:
type: string
semester:
type: string
specialControlLine:
type: number
state:
type: string
tuition:
type: string
updateBy:
type: string
updateTime:
type: string
type: object
host: localhost:8080
info:
contact: {}
description: 提供用户认证、院校专业、历年招生、计算专业的管理接口
title: 艺考招生管理系统 API
version: "2.0"
paths:
/auth/info:
get:
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取当前登录用户信息
tags:
- 认证
/auth/login:
post:
consumes:
- application/json
parameters:
- description: 登录信息
in: body
name: request
required: true
schema:
$ref: '#/definitions/controller.LoginRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 用户登录
tags:
- 认证
/auth/logout:
post:
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 用户登出
tags:
- 认证
/sys-users:
get:
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: size
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取用户列表
tags:
- 用户管理
post:
parameters:
- description: 用户信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.SysUser'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 创建用户
tags:
- 用户管理
/sys-users/{id}:
delete:
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 删除用户
tags:
- 用户管理
get:
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取单个用户
tags:
- 用户管理
patch:
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
- description: 要更新的字段
in: body
name: fields
required: true
schema:
additionalProperties: true
type: object
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 动态字段更新
tags:
- 用户管理
put:
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
- description: 用户信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.SysUser'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 更新用户
tags:
- 用户管理
/sys-users/{id}/password:
put:
parameters:
- description: 用户ID
in: path
name: id
required: true
type: string
- description: 密码信息
in: body
name: request
required: true
schema:
$ref: '#/definitions/controller.UpdatePasswordRequest'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 修改密码
tags:
- 用户管理
/yx-calculation-majors:
get:
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: size
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取计算专业列表
tags:
- 计算专业
post:
parameters:
- description: 计算专业信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxCalculationMajor'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 创建计算专业
tags:
- 计算专业
/yx-calculation-majors/{id}:
delete:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 删除计算专业
tags:
- 计算专业
get:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取单个计算专业
tags:
- 计算专业
patch:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 要更新的字段
in: body
name: fields
required: true
schema:
additionalProperties: true
type: object
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 动态字段更新
tags:
- 计算专业
put:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 计算专业信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxCalculationMajor'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 更新计算专业
tags:
- 计算专业
/yx-calculation-majors/batch:
delete:
parameters:
- description: ID列表
in: body
name: ids
required: true
schema:
items:
type: string
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量删除计算专业
tags:
- 计算专业
post:
parameters:
- description: 计算专业列表
in: body
name: items
required: true
schema:
items:
$ref: '#/definitions/entity.YxCalculationMajor'
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量创建计算专业
tags:
- 计算专业
/yx-history-enrolls:
get:
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: size
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取历年招生列表
tags:
- 历年招生
post:
parameters:
- description: 历年招生信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxHistoryMajorEnroll'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 创建历年招生记录
tags:
- 历年招生
/yx-history-enrolls/{id}:
delete:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 删除历年招生记录
tags:
- 历年招生
get:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取单个历年招生记录
tags:
- 历年招生
patch:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 要更新的字段
in: body
name: fields
required: true
schema:
additionalProperties: true
type: object
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 动态字段更新
tags:
- 历年招生
put:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 历年招生信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxHistoryMajorEnroll'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 更新历年招生记录
tags:
- 历年招生
/yx-history-enrolls/batch:
delete:
parameters:
- description: ID列表
in: body
name: ids
required: true
schema:
items:
type: string
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量删除历年招生记录
tags:
- 历年招生
post:
parameters:
- description: 历年招生列表
in: body
name: items
required: true
schema:
items:
$ref: '#/definitions/entity.YxHistoryMajorEnroll'
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量创建历年招生记录
tags:
- 历年招生
/yx-school-majors:
get:
parameters:
- description: 页码
in: query
name: page
type: integer
- description: 每页数量
in: query
name: size
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取院校专业列表
tags:
- 院校专业
post:
parameters:
- description: 院校专业信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxSchoolMajor'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 创建院校专业
tags:
- 院校专业
/yx-school-majors/{id}:
delete:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 删除院校专业
tags:
- 院校专业
get:
parameters:
- description: ID
in: path
name: id
required: true
type: string
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 获取单个院校专业
tags:
- 院校专业
patch:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 要更新的字段
in: body
name: fields
required: true
schema:
additionalProperties: true
type: object
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 动态字段更新
tags:
- 院校专业
put:
parameters:
- description: ID
in: path
name: id
required: true
type: string
- description: 院校专业信息
in: body
name: item
required: true
schema:
$ref: '#/definitions/entity.YxSchoolMajor'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 更新院校专业
tags:
- 院校专业
/yx-school-majors/batch:
delete:
parameters:
- description: ID列表
in: body
name: ids
required: true
schema:
items:
type: string
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量删除院校专业
tags:
- 院校专业
post:
parameters:
- description: 院校专业列表
in: body
name: items
required: true
schema:
items:
$ref: '#/definitions/entity.YxSchoolMajor'
type: array
responses:
"200":
description: OK
schema:
$ref: '#/definitions/common.Response'
summary: 批量创建院校专业
tags:
- 院校专业
securityDefinitions:
Bearer:
in: header
name: Authorization
type: apiKey
swagger: "2.0"

57
server/go.mod Normal file
View File

@ -0,0 +1,57 @@
module server
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.4.0
github.com/redis/go-redis/v9 v9.3.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.2
gorm.io/driver/mysql v1.5.2
gorm.io/gorm v1.25.5
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

188
server/go.sum Normal file
View File

@ -0,0 +1,188 @@
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0=
github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

132
server/main.go Normal file
View File

@ -0,0 +1,132 @@
// Package main 应用程序入口
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"server/common"
"server/config"
_ "server/docs"
"server/middleware"
sysController "server/modules/system/controller"
yxController "server/modules/yx/controller"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
// @title 艺考招生管理系统 API
// @version 2.0
// @description 提供用户认证、院校专业、历年招生、计算专业的管理接口
// @host localhost:8080
// @BasePath /api
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
func main() {
// 初始化日志
common.InitLogger()
common.Info("========== 应用启动 ==========")
// 初始化数据库
config.InitDB()
common.Info("数据库初始化完成")
// 初始化Redis
config.InitRedis()
common.Info("Redis初始化完成")
// 创建 Gin 引擎
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(gin.Recovery())
// 请求日志中间件
r.Use(requestLogMiddleware())
// Swagger 文档 (不需要登录)
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
// API 路由组
api := r.Group("/api")
// 中间件顺序: 安全校验 -> 限流 -> 登录鉴权
api.Use(middleware.SecurityMiddleware())
api.Use(middleware.RateLimitMiddleware())
api.Use(middleware.AuthMiddleware())
// 注册 System 模块路由
sysController.NewAuthController().RegisterRoutes(api)
sysController.NewSysUserController().RegisterRoutes(api)
// 注册 YX 模块路由
yxController.NewYxSchoolMajorController().RegisterRoutes(api)
yxController.NewYxHistoryMajorEnrollController().RegisterRoutes(api)
yxController.NewYxCalculationMajorController().RegisterRoutes(api)
// 创建 HTTP 服务器
srv := &http.Server{
Addr: ":8080",
Handler: r,
}
// 启动服务器
go func() {
common.Info("服务器启动: http://localhost:8080")
common.Info("Swagger文档: http://localhost:8080/swagger/index.html")
log.Println("服务器启动: http://localhost:8080")
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
common.LogError("启动失败: %s", err)
log.Fatalf("启动失败: %s\n", err)
}
}()
// 优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
common.Info("正在关闭服务器...")
log.Println("正在关闭服务器...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
common.LogError("服务器关闭异常: %v", err)
log.Fatal("服务器关闭异常:", err)
}
// 关闭资源
config.CloseDB()
config.CloseRedis()
common.Info("========== 应用关闭 ==========")
common.CloseLogger()
log.Println("服务器已退出")
}
// requestLogMiddleware 请求日志中间件
func requestLogMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
method := c.Request.Method
c.Next()
latency := time.Since(start)
status := c.Writer.Status()
clientIP := c.ClientIP()
common.Info("%s %s %d %v %s", method, path, status, latency, clientIP)
}
}

77
server/middleware/auth.go Normal file
View File

@ -0,0 +1,77 @@
// Package middleware 中间件
package middleware
import (
"strings"
"server/common"
"server/modules/system/service"
"github.com/gin-gonic/gin"
)
const (
// ContextUserKey 上下文中存储用户信息的key
ContextUserKey = "loginUser"
// TokenHeader 请求头中Token的key
TokenHeader = "Authorization"
// TokenPrefix Token前缀
TokenPrefix = "Bearer "
)
// 白名单路径 (不需要登录即可访问)
var whiteList = []string{
"/api/auth/login",
"/api/auth/register",
"/swagger/",
}
// AuthMiddleware 登录鉴权中间件
// 类似Java中的Shiro Filter
func AuthMiddleware() gin.HandlerFunc {
userService := service.NewSysUserService()
return func(c *gin.Context) {
path := c.Request.URL.Path
// 检查是否在白名单中
for _, white := range whiteList {
if strings.HasPrefix(path, white) {
c.Next()
return
}
}
// 获取Token
token := c.GetHeader(TokenHeader)
if token == "" {
common.Error(c, 401, "未登录")
c.Abort()
return
}
// 去除Bearer前缀
token = strings.TrimPrefix(token, TokenPrefix)
// if strings.HasPrefix(token, TokenPrefix) {
// token = token[len(TokenPrefix):]
// }
// 验证Token并获取用户信息
loginUser, err := userService.GetLoginUser(token)
if err != nil {
common.Error(c, 401, err.Error())
c.Abort()
return
}
// 将用户信息存入上下文
c.Set(ContextUserKey, loginUser)
c.Next()
}
}
// AddWhiteList 添加白名单路径
func AddWhiteList(paths ...string) {
whiteList = append(whiteList, paths...)
}

View File

@ -0,0 +1,142 @@
// Package middleware 限流中间件
package middleware
import (
"context"
"fmt"
"strings"
"time"
"server/common"
"server/config"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
)
// RateLimitMiddleware 限流中间件
// 基于 Redis 实现支持按用户ID或IP限流
// 不同接口可配置不同的限流规则
func RateLimitMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
cfg := config.AppConfig.RateLimit
// 未启用则跳过
if !cfg.Enable {
c.Next()
return
}
// 白名单路径跳过
path := c.Request.URL.Path
if isRateLimitWhitelist(path) {
c.Next()
return
}
// 获取限流规则
rule := getRule(path, cfg)
// 获取限流key (优先用户ID否则用IP)
key := getRateLimitKey(c, path)
// 检查是否超过限制
if !checkRateLimit(key, rule) {
common.Warn("请求过于频繁: Key=%s Path=%s", key, path)
c.JSON(429, map[string]interface{}{
"code": 429,
"message": "操作过快,请稍后再试",
"data": nil,
})
c.Abort()
return
}
c.Next()
}
}
// getRule 获取路径对应的限流规则
func getRule(path string, cfg config.RateLimitConfig) config.RateLimitRule {
// 精确匹配
if rule, ok := cfg.Rules[path]; ok {
return rule
}
// 前缀匹配
for rulePath, rule := range cfg.Rules {
if strings.HasPrefix(path, rulePath) {
return rule
}
}
// 返回默认规则
return cfg.Default
}
// getRateLimitKey 获取限流key
func getRateLimitKey(c *gin.Context, path string) string {
// 优先使用用户ID
if user := common.GetLoginUser(c); user != nil {
return fmt.Sprintf("ratelimit:%s:%s", user.ID, path)
}
// 否则使用IP
return fmt.Sprintf("ratelimit:%s:%s", c.ClientIP(), path)
}
// checkRateLimit 检查是否超过限流
// 使用 Redis 滑动窗口算法
func checkRateLimit(key string, rule config.RateLimitRule) bool {
ctx := context.Background()
rdb := config.RDB
now := time.Now().UnixMilli()
windowStart := now - int64(rule.Interval*1000)
// 使用 Redis 事务
pipe := rdb.Pipeline()
// 移除窗口外的记录
pipe.ZRemRangeByScore(ctx, key, "0", fmt.Sprintf("%d", windowStart))
// 获取当前窗口内的请求数
countCmd := pipe.ZCard(ctx, key)
// 添加当前请求
pipe.ZAdd(ctx, key, redis.Z{
Score: float64(now),
Member: fmt.Sprintf("%d", now),
})
// 设置过期时间
pipe.Expire(ctx, key, time.Duration(rule.Interval)*time.Second)
_, err := pipe.Exec(ctx)
if err != nil {
common.LogError("限流检查失败: %v", err)
return true // 出错时放行
}
count := countCmd.Val()
return count < int64(rule.MaxRequests)
}
// 限流白名单
var rateLimitWhitelist = []string{
"/swagger/",
"/api/auth/logout",
}
func isRateLimitWhitelist(path string) bool {
for _, white := range rateLimitWhitelist {
if strings.HasPrefix(path, white) {
return true
}
}
return false
}
// AddRateLimitWhitelist 添加限流白名单
func AddRateLimitWhitelist(paths ...string) {
rateLimitWhitelist = append(rateLimitWhitelist, paths...)
}

View File

@ -0,0 +1,110 @@
// Package middleware 安全校验中间件
package middleware
import (
"crypto/md5"
"encoding/hex"
"strconv"
"time"
"server/common"
"server/config"
"github.com/gin-gonic/gin"
)
// SecurityMiddleware 安全校验中间件
// 防止暴力入侵,校验请求头签名
// 请求头需携带:
// - X-App-Sign: 签名值 = MD5(timestamp + secretKey)
// - X-App-Timestamp: 时间戳(毫秒)
func SecurityMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
cfg := config.AppConfig.Security
// 未启用则跳过
if !cfg.Enable {
c.Next()
return
}
// 白名单路径跳过
path := c.Request.URL.Path
if isSecurityWhitelist(path) {
c.Next()
return
}
// 获取签名和时间戳
sign := c.GetHeader(cfg.HeaderKey)
timestamp := c.GetHeader("X-App-Timestamp")
if sign == "" || timestamp == "" {
common.Warn("安全校验失败: 缺少签名头 IP=%s Path=%s", c.ClientIP(), path)
common.Error(c, 403, "非法请求")
c.Abort()
return
}
// 验证时间戳 (5分钟内有效)
ts, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
common.Warn("安全校验失败: 时间戳格式错误 IP=%s", c.ClientIP())
common.Error(c, 403, "非法请求")
c.Abort()
return
}
now := time.Now().UnixMilli()
if abs(now-ts) > 5*60*1000 { // 5分钟
common.Warn("安全校验失败: 时间戳过期 IP=%s Timestamp=%d", c.ClientIP(), ts)
common.Error(c, 403, "请求已过期")
c.Abort()
return
}
// 验证签名
expectedSign := generateSign(timestamp, cfg.SecretKey)
if sign != expectedSign {
common.Warn("安全校验失败: 签名错误 IP=%s Sign=%s Expected=%s", c.ClientIP(), sign, expectedSign)
common.Error(c, 403, "签名错误")
c.Abort()
return
}
c.Next()
}
}
// generateSign 生成签名
func generateSign(timestamp, secretKey string) string {
data := timestamp + secretKey
hash := md5.Sum([]byte(data))
return hex.EncodeToString(hash[:])
}
// 安全校验白名单
var securityWhitelist = []string{
"/swagger/",
}
func isSecurityWhitelist(path string) bool {
for _, white := range securityWhitelist {
if len(path) >= len(white) && path[:len(white)] == white {
return true
}
}
return false
}
func abs(n int64) int64 {
if n < 0 {
return -n
}
return n
}
// AddSecurityWhitelist 添加安全校验白名单
func AddSecurityWhitelist(paths ...string) {
securityWhitelist = append(securityWhitelist, paths...)
}

View File

@ -0,0 +1,86 @@
// Package controller 控制器层
package controller
import (
"server/common"
"server/modules/system/service"
"github.com/gin-gonic/gin"
)
// LoginRequest 登录请求
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// AuthController 认证控制器
type AuthController struct {
userService *service.SysUserService
}
func NewAuthController() *AuthController {
return &AuthController{userService: service.NewSysUserService()}
}
func (ctrl *AuthController) RegisterRoutes(r *gin.RouterGroup) {
r.POST("/auth/login", ctrl.Login)
r.POST("/auth/logout", ctrl.Logout)
r.GET("/auth/info", ctrl.GetUserInfo)
}
// Login 用户登录
// @Summary 用户登录
// @Tags 认证
// @Accept json
// @Produce json
// @Param request body LoginRequest true "登录信息"
// @Success 200 {object} common.Response
// @Router /auth/login [post]
func (ctrl *AuthController) Login(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, 400, "用户名和密码不能为空")
return
}
loginUser, token, err := ctrl.userService.Login(req.Username, req.Password)
if err != nil {
common.Error(c, 401, err.Error())
return
}
common.Success(c, gin.H{
"token": token,
"user": loginUser,
})
}
// Logout 用户登出
// @Summary 用户登出
// @Tags 认证
// @Success 200 {object} common.Response
// @Router /auth/logout [post]
func (ctrl *AuthController) Logout(c *gin.Context) {
token := c.GetHeader("Authorization")
if len(token) > 7 {
token = token[7:] // 去除 "Bearer "
}
ctrl.userService.Logout(token)
common.Success(c, nil)
}
// GetUserInfo 获取当前登录用户信息
// @Summary 获取当前登录用户信息
// @Tags 认证
// @Success 200 {object} common.Response
// @Router /auth/info [get]
func (ctrl *AuthController) GetUserInfo(c *gin.Context) {
user := common.GetLoginUser(c)
if user == nil {
common.Error(c, 401, "未登录")
return
}
common.Success(c, user)
}

View File

@ -0,0 +1,161 @@
// Package controller 控制器层
package controller
import (
"strconv"
"server/common"
"server/modules/system/entity"
"server/modules/system/service"
"github.com/gin-gonic/gin"
)
type SysUserController struct {
service *service.SysUserService
}
func NewSysUserController() *SysUserController {
return &SysUserController{service: service.NewSysUserService()}
}
func (ctrl *SysUserController) RegisterRoutes(r *gin.RouterGroup) {
r.GET("/sys-users", ctrl.List)
r.GET("/sys-users/:id", ctrl.GetByID)
r.POST("/sys-users", ctrl.Create)
r.PUT("/sys-users/:id", ctrl.Update)
r.PATCH("/sys-users/:id", ctrl.UpdateFields)
r.DELETE("/sys-users/:id", ctrl.Delete)
r.PUT("/sys-users/:id/password", ctrl.UpdatePassword)
}
// @Summary 获取用户列表
// @Tags 用户管理
// @Param page query int false "页码"
// @Param size query int false "每页数量"
// @Success 200 {object} common.Response
// @Router /sys-users [get]
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"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
items, total, err := ctrl.service.List(page, size)
if err != nil {
common.Error(c, 500, err.Error())
return
}
common.SuccessPage(c, items, total, page, size)
}
// @Summary 获取单个用户
// @Tags 用户管理
// @Param id path string true "用户ID"
// @Success 200 {object} common.Response
// @Router /sys-users/{id} [get]
func (ctrl *SysUserController) GetByID(c *gin.Context) {
item, err := ctrl.service.GetByID(c.Param("id"))
if err != nil {
common.Error(c, 404, "未找到")
return
}
common.Success(c, item)
}
// @Summary 创建用户
// @Tags 用户管理
// @Param item body entity.SysUser true "用户信息"
// @Success 200 {object} common.Response
// @Router /sys-users [post]
func (ctrl *SysUserController) Create(c *gin.Context) {
var item entity.SysUser
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
// 设置创建人
item.CreateBy = common.GetLoginUserID(c)
if err := ctrl.service.Create(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 更新用户
// @Tags 用户管理
// @Param id path string true "用户ID"
// @Param item body entity.SysUser true "用户信息"
// @Success 200 {object} common.Response
// @Router /sys-users/{id} [put]
func (ctrl *SysUserController) Update(c *gin.Context) {
var item entity.SysUser
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
item.ID = c.Param("id")
item.UpdateBy = common.GetLoginUserID(c)
if err := ctrl.service.Update(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 动态字段更新
// @Tags 用户管理
// @Param id path string true "用户ID"
// @Param fields body map[string]interface{} true "要更新的字段"
// @Success 200 {object} common.Response
// @Router /sys-users/{id} [patch]
func (ctrl *SysUserController) UpdateFields(c *gin.Context) {
var fields map[string]interface{}
if err := c.ShouldBindJSON(&fields); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.UpdateFields(c.Param("id"), fields); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 删除用户
// @Tags 用户管理
// @Param id path string true "用户ID"
// @Success 200 {object} common.Response
// @Router /sys-users/{id} [delete]
func (ctrl *SysUserController) Delete(c *gin.Context) {
if err := ctrl.service.Delete(c.Param("id")); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// UpdatePasswordRequest 修改密码请求
type UpdatePasswordRequest struct {
OldPassword string `json:"oldPassword" binding:"required"`
NewPassword string `json:"newPassword" binding:"required"`
}
// @Summary 修改密码
// @Tags 用户管理
// @Param id path string true "用户ID"
// @Param request body UpdatePasswordRequest true "密码信息"
// @Success 200 {object} common.Response
// @Router /sys-users/{id}/password [put]
func (ctrl *SysUserController) UpdatePassword(c *gin.Context) {
var req UpdatePasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.UpdatePassword(c.Param("id"), req.OldPassword, req.NewPassword); err != nil {
common.Error(c, 400, err.Error())
return
}
common.Success(c, nil)
}

View File

@ -0,0 +1,55 @@
// Package entity 实体层
package entity
import "time"
// SysUser 用户表实体
type SysUser struct {
ID string `gorm:"column:id;primaryKey" json:"id"`
Username string `gorm:"column:username" json:"username"` // 登录账号
Realname string `gorm:"column:realname" json:"realname"` // 真实姓名
Password string `gorm:"column:password" json:"-"` // 密码 (不返回给前端)
Salt string `gorm:"column:salt" json:"-"` // md5密码盐
Avatar string `gorm:"column:avatar" json:"avatar"` // 头像
Birthday *time.Time `gorm:"column:birthday" json:"birthday"` // 生日
Sex int `gorm:"column:sex" json:"sex"` // 性别(0-未知,1-男,2-女)
Email string `gorm:"column:email" json:"email"` // 电子邮件
Phone string `gorm:"column:phone" json:"phone"` // 电话
OrgCode string `gorm:"column:org_code" json:"orgCode"` // 机构编码
Status int `gorm:"column:status" json:"status"` // 状态(1-正常,2-冻结)
DelFlag int `gorm:"column:del_flag" json:"delFlag"` // 删除状态(0-正常,1-已删除)
ThirdID string `gorm:"column:third_id" json:"thirdId"` // 第三方登录唯一标识
ThirdType string `gorm:"column:third_type" json:"thirdType"` // 第三方类型
ActivitiSync int `gorm:"column:activiti_sync" json:"activitiSync"` // 同步工作流引擎
WorkNo string `gorm:"column:work_no" json:"workNo"` // 工号
Telephone string `gorm:"column:telephone" json:"telephone"` // 座机号
CreateBy string `gorm:"column:create_by" json:"createBy"` // 创建人
CreateTime *time.Time `gorm:"column:create_time" json:"createTime"` // 创建时间
UpdateBy string `gorm:"column:update_by" json:"updateBy"` // 更新人
UpdateTime *time.Time `gorm:"column:update_time" json:"updateTime"` // 更新时间
UserIdentity int `gorm:"column:user_identity" json:"userIdentity"` // 身份(1普通成员 2上级)
DepartIds string `gorm:"column:depart_ids" json:"departIds"` // 负责部门
ClientID string `gorm:"column:client_id" json:"clientId"` // 设备ID
LoginTenantID int `gorm:"column:login_tenant_id" json:"loginTenantId"` // 上次登录租户ID
BpmStatus string `gorm:"column:bpm_status" json:"bpmStatus"` // 流程状态
WxOpenID string `gorm:"column:wx_open_id" json:"wxOpenId"` // 微信openId
DyOpenID string `gorm:"column:dy_open_id" json:"dyOpenId"` // 抖音openId
IP string `gorm:"column:ip" json:"ip"` // 注册时ip
ShowLinediff string `gorm:"column:show_linediff" json:"showLinediff"` // 是否显示历年线差
ProgramType string `gorm:"column:program_type" json:"programType"` // 所属程序
}
func (SysUser) TableName() string {
return "sys_user"
}
// LoginUser 登录用户信息 (存储在Redis中)
type LoginUser 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 string `json:"token"`
}

View File

@ -0,0 +1,56 @@
// Package mapper 数据访问层
package mapper
import (
"server/config"
"server/modules/system/entity"
)
type SysUserMapper struct{}
func NewSysUserMapper() *SysUserMapper {
return &SysUserMapper{}
}
func (m *SysUserMapper) FindAll(page, size int) ([]entity.SysUser, int64, error) {
var items []entity.SysUser
var total int64
config.DB.Model(&entity.SysUser{}).Where("del_flag = 0").Count(&total)
err := config.DB.Where("del_flag = 0").Offset((page - 1) * size).Limit(size).Find(&items).Error
return items, total, err
}
func (m *SysUserMapper) FindByID(id string) (*entity.SysUser, error) {
var item entity.SysUser
err := config.DB.First(&item, "id = ? AND del_flag = 0", id).Error
return &item, err
}
func (m *SysUserMapper) FindByUsername(username string) (*entity.SysUser, error) {
var item entity.SysUser
err := config.DB.First(&item, "username = ? AND del_flag = 0", username).Error
return &item, err
}
func (m *SysUserMapper) FindByPhone(phone string) (*entity.SysUser, error) {
var item entity.SysUser
err := config.DB.First(&item, "phone = ? AND del_flag = 0", phone).Error
return &item, err
}
func (m *SysUserMapper) Create(item *entity.SysUser) error {
return config.DB.Create(item).Error
}
func (m *SysUserMapper) Update(item *entity.SysUser) error {
return config.DB.Save(item).Error
}
func (m *SysUserMapper) UpdateFields(id string, fields map[string]interface{}) error {
return config.DB.Model(&entity.SysUser{}).Where("id = ?", id).Updates(fields).Error
}
func (m *SysUserMapper) Delete(id string) error {
// 逻辑删除
return config.DB.Model(&entity.SysUser{}).Where("id = ?", id).Update("del_flag", 1).Error
}

View File

@ -0,0 +1,189 @@
// Package service 业务逻辑层
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"time"
"server/common"
"server/config"
"server/modules/system/entity"
"server/modules/system/mapper"
"github.com/google/uuid"
)
const (
TokenPrefix = "login:token:" // Redis中Token前缀
TokenExpire = 24 * time.Hour // Token过期时间
)
type SysUserService struct {
mapper *mapper.SysUserMapper
}
func NewSysUserService() *SysUserService {
return &SysUserService{mapper: mapper.NewSysUserMapper()}
}
// Login 用户登录
// 密码验证方式与 Java JeecgBoot 兼容: PBEWithMD5AndDES(username, password, salt)
func (s *SysUserService) Login(username, password string) (*entity.LoginUser, string, error) {
// 查询用户
user, err := s.mapper.FindByUsername(username)
if err != nil {
return nil, "", errors.New("用户不存在")
}
// 验证状态
if user.Status == 2 {
return nil, "", errors.New("账号已被冻结")
}
// 验证密码 (与Java兼容: encrypt(username, password, salt))
encrypted, err := common.Encrypt(username, password, user.Salt)
if (user.Password != encrypted) || (err != nil) {
return nil, "", errors.New("用户名或密码错误")
}
// 生成Token
token := s.generateToken()
// 构建登录用户信息
loginUser := &entity.LoginUser{
ID: user.ID,
Username: user.Username,
Realname: user.Realname,
Avatar: user.Avatar,
Phone: user.Phone,
Email: user.Email,
Token: token,
}
// 存储到Redis
if err := s.saveLoginUser(token, loginUser); err != nil {
return nil, "", errors.New("登录失败,请重试")
}
return loginUser, token, nil
}
// Logout 用户登出
func (s *SysUserService) Logout(token string) error {
ctx := context.Background()
return config.RDB.Del(ctx, TokenPrefix+token).Err()
}
// GetLoginUser 根据Token获取登录用户信息
func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
ctx := context.Background()
data, err := config.RDB.Get(ctx, TokenPrefix+token).Result()
if err != nil {
return nil, errors.New("未登录或登录已过期")
}
var loginUser entity.LoginUser
if err := json.Unmarshal([]byte(data), &loginUser); err != nil {
return nil, errors.New("登录信息异常")
}
// 刷新过期时间
config.RDB.Expire(ctx, TokenPrefix+token, TokenExpire)
return &loginUser, nil
}
// RefreshToken 刷新Token过期时间
func (s *SysUserService) RefreshToken(token string) error {
ctx := context.Background()
return config.RDB.Expire(ctx, TokenPrefix+token, TokenExpire).Err()
}
// 保存登录用户到Redis
func (s *SysUserService) saveLoginUser(token string, user *entity.LoginUser) error {
ctx := context.Background()
data, err := json.Marshal(user)
if err != nil {
return err
}
return config.RDB.Set(ctx, TokenPrefix+token, data, TokenExpire).Err()
}
// 生成Token
func (s *SysUserService) generateToken() string {
return uuid.New().String()
}
// ========== 用户管理 ==========
func (s *SysUserService) List(page, size int) ([]entity.SysUser, int64, error) {
return s.mapper.FindAll(page, size)
}
func (s *SysUserService) GetByID(id string) (*entity.SysUser, error) {
return s.mapper.FindByID(id)
}
func (s *SysUserService) Create(item *entity.SysUser) error {
item.ID = uuid.New().String()
// 生成盐值 (8字节)
item.Salt = uuid.New().String()[:8]
// 加密密码 (与Java兼容)
encrypted, err := common.Encrypt(item.Username, item.Password, item.Salt)
if err != nil {
log.Printf("密码加密失败: %v", err)
return fmt.Errorf("密码加密失败: %w,请联系管理员", err) // 仍然返回错误
}
item.Password = encrypted
item.DelFlag = 0
item.Status = 1
now := time.Now()
item.CreateTime = &now
return s.mapper.Create(item)
}
func (s *SysUserService) Update(item *entity.SysUser) error {
now := time.Now()
item.UpdateTime = &now
return s.mapper.Update(item)
}
func (s *SysUserService) UpdateFields(id string, fields map[string]interface{}) error {
return s.mapper.UpdateFields(id, fields)
}
func (s *SysUserService) Delete(id string) error {
return s.mapper.Delete(id)
}
// UpdatePassword 修改密码
func (s *SysUserService) UpdatePassword(id, oldPwd, newPwd string) error {
user, err := s.mapper.FindByID(id)
if err != nil {
return errors.New("用户不存在")
}
// 验证旧密码
encrypted, err := common.Encrypt(user.Username, oldPwd, user.Salt)
if err != nil {
log.Printf("密码加密失败: %v", err)
return fmt.Errorf("密码加密失败: %w,请联系管理员", err) // 仍然返回错误
}
if encrypted != user.Password {
return errors.New("原密码错误")
}
// 生成新密码
newEncrypted, err := common.Encrypt(user.Username, newPwd, user.Salt)
if err != nil {
log.Printf("密码加密失败: %v", err)
return fmt.Errorf("密码加密失败: %w,请联系管理员", err) // 仍然返回错误
}
return s.mapper.UpdateFields(id, map[string]interface{}{
"password": newEncrypted,
})
}

View File

@ -0,0 +1,168 @@
// Package controller 控制器层
package controller
import (
"strconv"
"server/common"
"server/modules/yx/entity"
"server/modules/yx/service"
"github.com/gin-gonic/gin"
)
type YxCalculationMajorController struct {
service *service.YxCalculationMajorService
}
func NewYxCalculationMajorController() *YxCalculationMajorController {
return &YxCalculationMajorController{service: service.NewYxCalculationMajorService()}
}
func (ctrl *YxCalculationMajorController) RegisterRoutes(r *gin.RouterGroup) {
r.GET("/yx-calculation-majors", ctrl.List)
r.GET("/yx-calculation-majors/:id", ctrl.GetByID)
r.POST("/yx-calculation-majors", ctrl.Create)
r.PUT("/yx-calculation-majors/:id", ctrl.Update)
r.PATCH("/yx-calculation-majors/:id", ctrl.UpdateFields)
r.DELETE("/yx-calculation-majors/:id", ctrl.Delete)
r.POST("/yx-calculation-majors/batch", ctrl.BatchCreate)
r.DELETE("/yx-calculation-majors/batch", ctrl.BatchDelete)
}
// @Summary 获取计算专业列表
// @Tags 计算专业
// @Param page query int false "页码"
// @Param size query int false "每页数量"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors [get]
func (ctrl *YxCalculationMajorController) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
items, total, err := ctrl.service.List(page, size)
if err != nil {
common.Error(c, 500, err.Error())
return
}
common.SuccessPage(c, items, total, page, size)
}
// @Summary 获取单个计算专业
// @Tags 计算专业
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/{id} [get]
func (ctrl *YxCalculationMajorController) GetByID(c *gin.Context) {
item, err := ctrl.service.GetByID(c.Param("id"))
if err != nil {
common.Error(c, 404, "未找到")
return
}
common.Success(c, item)
}
// @Summary 创建计算专业
// @Tags 计算专业
// @Param item body entity.YxCalculationMajor true "计算专业信息"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors [post]
func (ctrl *YxCalculationMajorController) Create(c *gin.Context) {
var item entity.YxCalculationMajor
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.Create(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 更新计算专业
// @Tags 计算专业
// @Param id path string true "ID"
// @Param item body entity.YxCalculationMajor true "计算专业信息"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/{id} [put]
func (ctrl *YxCalculationMajorController) Update(c *gin.Context) {
var item entity.YxCalculationMajor
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
item.ID = c.Param("id")
if err := ctrl.service.Update(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 动态字段更新
// @Tags 计算专业
// @Param id path string true "ID"
// @Param fields body map[string]interface{} true "要更新的字段"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/{id} [patch]
func (ctrl *YxCalculationMajorController) UpdateFields(c *gin.Context) {
var fields map[string]interface{}
if err := c.ShouldBindJSON(&fields); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.UpdateFields(c.Param("id"), fields); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 删除计算专业
// @Tags 计算专业
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/{id} [delete]
func (ctrl *YxCalculationMajorController) Delete(c *gin.Context) {
if err := ctrl.service.Delete(c.Param("id")); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 批量创建计算专业
// @Tags 计算专业
// @Param items body []entity.YxCalculationMajor true "计算专业列表"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/batch [post]
func (ctrl *YxCalculationMajorController) BatchCreate(c *gin.Context) {
var items []entity.YxCalculationMajor
if err := c.ShouldBindJSON(&items); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchCreate(items); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 批量删除计算专业
// @Tags 计算专业
// @Param ids body []string true "ID列表"
// @Success 200 {object} common.Response
// @Router /yx-calculation-majors/batch [delete]
func (ctrl *YxCalculationMajorController) BatchDelete(c *gin.Context) {
var ids []string
if err := c.ShouldBindJSON(&ids); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchDelete(ids); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}

View File

@ -0,0 +1,168 @@
// Package controller 控制器层
package controller
import (
"strconv"
"server/common"
"server/modules/yx/entity"
"server/modules/yx/service"
"github.com/gin-gonic/gin"
)
type YxHistoryMajorEnrollController struct {
service *service.YxHistoryMajorEnrollService
}
func NewYxHistoryMajorEnrollController() *YxHistoryMajorEnrollController {
return &YxHistoryMajorEnrollController{service: service.NewYxHistoryMajorEnrollService()}
}
func (ctrl *YxHistoryMajorEnrollController) RegisterRoutes(r *gin.RouterGroup) {
r.GET("/yx-history-enrolls", ctrl.List)
r.GET("/yx-history-enrolls/:id", ctrl.GetByID)
r.POST("/yx-history-enrolls", ctrl.Create)
r.PUT("/yx-history-enrolls/:id", ctrl.Update)
r.PATCH("/yx-history-enrolls/:id", ctrl.UpdateFields)
r.DELETE("/yx-history-enrolls/:id", ctrl.Delete)
r.POST("/yx-history-enrolls/batch", ctrl.BatchCreate)
r.DELETE("/yx-history-enrolls/batch", ctrl.BatchDelete)
}
// @Summary 获取历年招生列表
// @Tags 历年招生
// @Param page query int false "页码"
// @Param size query int false "每页数量"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls [get]
func (ctrl *YxHistoryMajorEnrollController) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
items, total, err := ctrl.service.List(page, size)
if err != nil {
common.Error(c, 500, err.Error())
return
}
common.SuccessPage(c, items, total, page, size)
}
// @Summary 获取单个历年招生记录
// @Tags 历年招生
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/{id} [get]
func (ctrl *YxHistoryMajorEnrollController) GetByID(c *gin.Context) {
item, err := ctrl.service.GetByID(c.Param("id"))
if err != nil {
common.Error(c, 404, "未找到")
return
}
common.Success(c, item)
}
// @Summary 创建历年招生记录
// @Tags 历年招生
// @Param item body entity.YxHistoryMajorEnroll true "历年招生信息"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls [post]
func (ctrl *YxHistoryMajorEnrollController) Create(c *gin.Context) {
var item entity.YxHistoryMajorEnroll
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.Create(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 更新历年招生记录
// @Tags 历年招生
// @Param id path string true "ID"
// @Param item body entity.YxHistoryMajorEnroll true "历年招生信息"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/{id} [put]
func (ctrl *YxHistoryMajorEnrollController) Update(c *gin.Context) {
var item entity.YxHistoryMajorEnroll
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
item.ID = c.Param("id")
if err := ctrl.service.Update(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// @Summary 动态字段更新
// @Tags 历年招生
// @Param id path string true "ID"
// @Param fields body map[string]interface{} true "要更新的字段"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/{id} [patch]
func (ctrl *YxHistoryMajorEnrollController) UpdateFields(c *gin.Context) {
var fields map[string]interface{}
if err := c.ShouldBindJSON(&fields); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.UpdateFields(c.Param("id"), fields); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 删除历年招生记录
// @Tags 历年招生
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/{id} [delete]
func (ctrl *YxHistoryMajorEnrollController) Delete(c *gin.Context) {
if err := ctrl.service.Delete(c.Param("id")); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 批量创建历年招生记录
// @Tags 历年招生
// @Param items body []entity.YxHistoryMajorEnroll true "历年招生列表"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/batch [post]
func (ctrl *YxHistoryMajorEnrollController) BatchCreate(c *gin.Context) {
var items []entity.YxHistoryMajorEnroll
if err := c.ShouldBindJSON(&items); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchCreate(items); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// @Summary 批量删除历年招生记录
// @Tags 历年招生
// @Param ids body []string true "ID列表"
// @Success 200 {object} common.Response
// @Router /yx-history-enrolls/batch [delete]
func (ctrl *YxHistoryMajorEnrollController) BatchDelete(c *gin.Context) {
var ids []string
if err := c.ShouldBindJSON(&ids); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchDelete(ids); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}

View File

@ -0,0 +1,176 @@
// Package controller 控制器层
package controller
import (
"strconv"
"server/common"
"server/modules/yx/entity"
"server/modules/yx/service"
"github.com/gin-gonic/gin"
)
type YxSchoolMajorController struct {
service *service.YxSchoolMajorService
}
func NewYxSchoolMajorController() *YxSchoolMajorController {
return &YxSchoolMajorController{service: service.NewYxSchoolMajorService()}
}
func (ctrl *YxSchoolMajorController) RegisterRoutes(r *gin.RouterGroup) {
r.GET("/yx-school-majors", ctrl.List)
r.GET("/yx-school-majors/:id", ctrl.GetByID)
r.POST("/yx-school-majors", ctrl.Create)
r.PUT("/yx-school-majors/:id", ctrl.Update)
r.PATCH("/yx-school-majors/:id", ctrl.UpdateFields)
r.DELETE("/yx-school-majors/:id", ctrl.Delete)
r.POST("/yx-school-majors/batch", ctrl.BatchCreate)
r.DELETE("/yx-school-majors/batch", ctrl.BatchDelete)
}
// List 获取院校专业列表
// @Summary 获取院校专业列表
// @Tags 院校专业
// @Param page query int false "页码"
// @Param size query int false "每页数量"
// @Success 200 {object} common.Response
// @Router /yx-school-majors [get]
func (ctrl *YxSchoolMajorController) List(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
items, total, err := ctrl.service.List(page, size)
if err != nil {
common.Error(c, 500, err.Error())
return
}
common.SuccessPage(c, items, total, page, size)
}
// GetByID 获取单个院校专业
// @Summary 获取单个院校专业
// @Tags 院校专业
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/{id} [get]
func (ctrl *YxSchoolMajorController) GetByID(c *gin.Context) {
item, err := ctrl.service.GetByID(c.Param("id"))
if err != nil {
common.Error(c, 404, "未找到")
return
}
common.Success(c, item)
}
// Create 创建院校专业
// @Summary 创建院校专业
// @Tags 院校专业
// @Param item body entity.YxSchoolMajor true "院校专业信息"
// @Success 200 {object} common.Response
// @Router /yx-school-majors [post]
func (ctrl *YxSchoolMajorController) Create(c *gin.Context) {
var item entity.YxSchoolMajor
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.Create(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// Update 更新院校专业
// @Summary 更新院校专业
// @Tags 院校专业
// @Param id path string true "ID"
// @Param item body entity.YxSchoolMajor true "院校专业信息"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/{id} [put]
func (ctrl *YxSchoolMajorController) Update(c *gin.Context) {
var item entity.YxSchoolMajor
if err := c.ShouldBindJSON(&item); err != nil {
common.Error(c, 400, "参数错误")
return
}
item.ID = c.Param("id")
if err := ctrl.service.Update(&item); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, item)
}
// UpdateFields 动态字段更新
// @Summary 动态字段更新
// @Tags 院校专业
// @Param id path string true "ID"
// @Param fields body map[string]interface{} true "要更新的字段"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/{id} [patch]
func (ctrl *YxSchoolMajorController) UpdateFields(c *gin.Context) {
var fields map[string]interface{}
if err := c.ShouldBindJSON(&fields); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.UpdateFields(c.Param("id"), fields); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// Delete 删除院校专业
// @Summary 删除院校专业
// @Tags 院校专业
// @Param id path string true "ID"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/{id} [delete]
func (ctrl *YxSchoolMajorController) Delete(c *gin.Context) {
if err := ctrl.service.Delete(c.Param("id")); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// BatchCreate 批量创建
// @Summary 批量创建院校专业
// @Tags 院校专业
// @Param items body []entity.YxSchoolMajor true "院校专业列表"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/batch [post]
func (ctrl *YxSchoolMajorController) BatchCreate(c *gin.Context) {
var items []entity.YxSchoolMajor
if err := c.ShouldBindJSON(&items); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchCreate(items); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}
// BatchDelete 批量删除
// @Summary 批量删除院校专业
// @Tags 院校专业
// @Param ids body []string true "ID列表"
// @Success 200 {object} common.Response
// @Router /yx-school-majors/batch [delete]
func (ctrl *YxSchoolMajorController) BatchDelete(c *gin.Context) {
var ids []string
if err := c.ShouldBindJSON(&ids); err != nil {
common.Error(c, 400, "参数错误")
return
}
if err := ctrl.service.BatchDelete(ids); err != nil {
common.Error(c, 500, err.Error())
return
}
common.Success(c, nil)
}

View File

@ -0,0 +1,40 @@
// Package entity 实体层
package entity
import "time"
// YxCalculationMajor 计算专业表实体
type YxCalculationMajor struct {
ID string `gorm:"column:id;primaryKey" json:"id"`
ScoreID string `gorm:"column:score_id" json:"scoreId"`
SchoolCode string `gorm:"column:school_code" json:"schoolCode"`
MajorCode string `gorm:"column:major_code" json:"majorCode"`
MajorName string `gorm:"column:major_name" json:"majorName"`
EnrollmentCode string `gorm:"column:enrollment_code" json:"enrollmentCode"`
Tuition string `gorm:"column:tuition" json:"tuition"`
Detail string `gorm:"column:detail" json:"detail"`
Category string `gorm:"column:category" json:"category"`
RulesEnrollProbability string `gorm:"column:rules_enroll_probability" json:"rulesEnrollProbability"`
Batch string `gorm:"column:batch" json:"batch"`
StudentOldConvertedScore float64 `gorm:"column:student_old_converted_score" json:"studentOldConvertedScore"`
StudentConvertedScore float64 `gorm:"column:student_converted_score" json:"studentConvertedScore"`
EnrollProbability float64 `gorm:"column:enroll_probability" json:"enrollProbability"`
ProbabilityOperator string `gorm:"column:probability_operator" json:"probabilityOperator"`
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
MajorType string `gorm:"column:major_type" json:"majorType"`
MajorTypeChild string `gorm:"column:major_type_child" json:"majorTypeChild"`
PlanNum int `gorm:"column:plan_num" json:"planNum"`
MainSubjects string `gorm:"column:main_subjects" json:"mainSubjects"`
Limitation string `gorm:"column:limitation" json:"limitation"`
OtherScoreLimitation string `gorm:"column:other_score_limitation" json:"otherScoreLimitation"`
RulesEnrollProbabilitySx string `gorm:"column:rules_enroll_probability_sx" json:"rulesEnrollProbabilitySx"`
Kslx string `gorm:"column:kslx" json:"kslx"`
PrivateStudentConvertedScore float64 `gorm:"column:private_student_converted_score" json:"privateStudentConvertedScore"`
PrivateRulesEnrollProbability string `gorm:"column:private_rules_enroll_probability" json:"privateRulesEnrollProbability"`
PrivateProbabilityOperator string `gorm:"column:private_probability_operator" json:"privateProbabilityOperator"`
State string `gorm:"column:state" json:"state"`
}
func (YxCalculationMajor) TableName() string {
return "yx_calculation_major_2025_2"
}

View File

@ -0,0 +1,42 @@
// Package entity 实体层
package entity
import "time"
// YxHistoryMajorEnroll 历年艺术类招生录取分数表实体
type YxHistoryMajorEnroll struct {
ID string `gorm:"column:id;primaryKey" json:"id"`
SchoolCode string `gorm:"column:school_code" json:"schoolCode"`
SchoolName string `gorm:"column:school_name" json:"schoolName"`
InstitutionCode string `gorm:"column:institution_code" json:"institutionCode"`
MajorCode string `gorm:"column:major_code" json:"majorCode"`
MajorName string `gorm:"column:major_name" json:"majorName"`
MajorType string `gorm:"column:major_type" json:"majorType"`
EnrollmentCode string `gorm:"column:enrollment_code" json:"enrollmentCode"`
Category string `gorm:"column:category" json:"category"`
Year string `gorm:"column:year" json:"year"`
EnrollNum int `gorm:"column:enroll_num" json:"enrollNum"`
ScoreLineDifference float64 `gorm:"column:score_line_difference" json:"scoreLineDifference"`
CreateBy string `gorm:"column:create_by" json:"createBy"`
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
UpdateBy string `gorm:"column:update_by" json:"updateBy"`
UpdateTime time.Time `gorm:"column:update_time" json:"updateTime"`
SysOrgCode string `gorm:"column:sys_org_code" json:"sysOrgCode"`
Detail string `gorm:"column:detail" json:"detail"`
RulesEnrollProbability string `gorm:"column:rules_enroll_probability" json:"rulesEnrollProbability"`
ControlLine float64 `gorm:"column:control_line" json:"controlLine"`
AdmissionLine float64 `gorm:"column:admission_line" json:"admissionLine"`
ProbabilityOperator string `gorm:"column:probability_operator" json:"probabilityOperator"`
Batch string `gorm:"column:batch" json:"batch"`
OneVolunteerAdmissionNum int `gorm:"column:one_volunteer_admission_num" json:"oneVolunteerAdmissionNum"`
AdmissionNum int `gorm:"column:admission_num" json:"admissionNum"`
ActualPitcherNum int `gorm:"column:actual_pitcher_num" json:"actualPitcherNum"`
CheckMaster string `gorm:"column:check_master" json:"checkMaster"`
MajorTypeChild string `gorm:"column:major_type_child" json:"majorTypeChild"`
MainSubjects string `gorm:"column:main_subjects" json:"mainSubjects"`
Tuition string `gorm:"column:tuition" json:"tuition"`
}
func (YxHistoryMajorEnroll) TableName() string {
return "yx_history_major_enroll"
}

View File

@ -0,0 +1,46 @@
// Package entity 实体层
package entity
import "time"
// YxSchoolMajor 院校专业关联表实体
type YxSchoolMajor struct {
ID string `gorm:"column:id;primaryKey" json:"id"`
SchoolCode string `gorm:"column:school_code" json:"schoolCode"`
SchoolName string `gorm:"column:school_name" json:"schoolName"`
MajorCode string `gorm:"column:major_code" json:"majorCode"`
MajorName string `gorm:"column:major_name" json:"majorName"`
MajorType string `gorm:"column:major_type" json:"majorType"`
MajorTypeChild string `gorm:"column:major_type_child" json:"majorTypeChild"`
MainSubjects string `gorm:"column:main_subjects" json:"mainSubjects"`
EnrollmentCode string `gorm:"column:enrollment_code" json:"enrollmentCode"`
Category string `gorm:"column:category" json:"category"`
Batch string `gorm:"column:batch" json:"batch"`
Tuition string `gorm:"column:tuition" json:"tuition"`
PlanNum int `gorm:"column:plan_num" json:"planNum"`
Detail string `gorm:"column:detail" json:"detail"`
Semester string `gorm:"column:semester" json:"semester"`
CreateBy string `gorm:"column:create_by" json:"createBy"`
CreateTime time.Time `gorm:"column:create_time" json:"createTime"`
UpdateBy string `gorm:"column:update_by" json:"updateBy"`
UpdateTime time.Time `gorm:"column:update_time" json:"updateTime"`
RulesEnrollProbabilitySx string `gorm:"column:rules_enroll_probability_sx" json:"rulesEnrollProbabilitySx"`
RulesEnrollProbability string `gorm:"column:rules_enroll_probability" json:"rulesEnrollProbability"`
ProbabilityOperator string `gorm:"column:probability_operator" json:"probabilityOperator"`
CulturalControlLine float64 `gorm:"column:cultural_control_line" json:"culturalControlLine"`
SpecialControlLine float64 `gorm:"column:special_control_line" json:"specialControlLine"`
CheckMaster string `gorm:"column:check_master" json:"checkMaster"`
Limitation string `gorm:"column:limitation" json:"limitation"`
ProfessionalScoreLimitation float64 `gorm:"column:professional_score_limitation" json:"professionalScoreLimitation"`
EnglishScoreLimitation float64 `gorm:"column:english_score_limitation" json:"englishScoreLimitation"`
ChineseScoreLimitation float64 `gorm:"column:chinese_score_limitation" json:"chineseScoreLimitation"`
CulturalScoreLimitation float64 `gorm:"column:cultural_score_limitation" json:"culturalScoreLimitation"`
Kslx string `gorm:"column:kslx" json:"kslx"`
PrivateProbabilityOperator string `gorm:"column:private_probability_operator" json:"privateProbabilityOperator"`
PrivateRulesEnrollProbability string `gorm:"column:private_rules_enroll_probability" json:"privateRulesEnrollProbability"`
State string `gorm:"column:state" json:"state"`
}
func (YxSchoolMajor) TableName() string {
return "yx_school_major"
}

View File

@ -0,0 +1,74 @@
// Package mapper 数据访问层
package mapper
import (
"server/config"
"server/modules/yx/entity"
"gorm.io/gorm/clause"
)
type YxCalculationMajorMapper struct{}
func NewYxCalculationMajorMapper() *YxCalculationMajorMapper {
return &YxCalculationMajorMapper{}
}
func (m *YxCalculationMajorMapper) FindAll(page, size int) ([]entity.YxCalculationMajor, int64, error) {
var items []entity.YxCalculationMajor
var total int64
config.DB.Model(&entity.YxCalculationMajor{}).Count(&total)
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
return items, total, err
}
func (m *YxCalculationMajorMapper) FindByID(id string) (*entity.YxCalculationMajor, error) {
var item entity.YxCalculationMajor
err := config.DB.First(&item, "id = ?", id).Error
return &item, err
}
func (m *YxCalculationMajorMapper) Create(item *entity.YxCalculationMajor) error {
return config.DB.Create(item).Error
}
func (m *YxCalculationMajorMapper) Update(item *entity.YxCalculationMajor) error {
return config.DB.Save(item).Error
}
func (m *YxCalculationMajorMapper) UpdateFields(id string, fields map[string]interface{}) error {
return config.DB.Model(&entity.YxCalculationMajor{}).Where("id = ?", id).Updates(fields).Error
}
func (m *YxCalculationMajorMapper) Delete(id string) error {
return config.DB.Delete(&entity.YxCalculationMajor{}, "id = ?", id).Error
}
func (m *YxCalculationMajorMapper) FindByScoreID(scoreID string) ([]entity.YxCalculationMajor, error) {
var items []entity.YxCalculationMajor
err := config.DB.Where("score_id = ?", scoreID).Find(&items).Error
return items, err
}
func (m *YxCalculationMajorMapper) BatchCreate(items []entity.YxCalculationMajor, batchSize int) error {
return config.DB.CreateInBatches(items, batchSize).Error
}
func (m *YxCalculationMajorMapper) BatchUpdate(items []entity.YxCalculationMajor) error {
return config.DB.Save(items).Error
}
func (m *YxCalculationMajorMapper) BatchUpsert(items []entity.YxCalculationMajor, updateColumns []string) error {
return config.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns(updateColumns),
}).CreateInBatches(items, 100).Error
}
func (m *YxCalculationMajorMapper) BatchDelete(ids []string) error {
return config.DB.Delete(&entity.YxCalculationMajor{}, "id IN ?", ids).Error
}
func (m *YxCalculationMajorMapper) DeleteByScoreID(scoreID string) error {
return config.DB.Delete(&entity.YxCalculationMajor{}, "score_id = ?", scoreID).Error
}

View File

@ -0,0 +1,70 @@
// Package mapper 数据访问层
package mapper
import (
"server/config"
"server/modules/yx/entity"
"gorm.io/gorm/clause"
)
type YxHistoryMajorEnrollMapper struct{}
func NewYxHistoryMajorEnrollMapper() *YxHistoryMajorEnrollMapper {
return &YxHistoryMajorEnrollMapper{}
}
func (m *YxHistoryMajorEnrollMapper) FindAll(page, size int) ([]entity.YxHistoryMajorEnroll, int64, error) {
var items []entity.YxHistoryMajorEnroll
var total int64
config.DB.Model(&entity.YxHistoryMajorEnroll{}).Count(&total)
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
return items, total, err
}
func (m *YxHistoryMajorEnrollMapper) FindByID(id string) (*entity.YxHistoryMajorEnroll, error) {
var item entity.YxHistoryMajorEnroll
err := config.DB.First(&item, "id = ?", id).Error
return &item, err
}
func (m *YxHistoryMajorEnrollMapper) Create(item *entity.YxHistoryMajorEnroll) error {
return config.DB.Create(item).Error
}
func (m *YxHistoryMajorEnrollMapper) Update(item *entity.YxHistoryMajorEnroll) error {
return config.DB.Save(item).Error
}
func (m *YxHistoryMajorEnrollMapper) UpdateFields(id string, fields map[string]interface{}) error {
return config.DB.Model(&entity.YxHistoryMajorEnroll{}).Where("id = ?", id).Updates(fields).Error
}
func (m *YxHistoryMajorEnrollMapper) Delete(id string) error {
return config.DB.Delete(&entity.YxHistoryMajorEnroll{}, "id = ?", id).Error
}
func (m *YxHistoryMajorEnrollMapper) FindByYear(year string) ([]entity.YxHistoryMajorEnroll, error) {
var items []entity.YxHistoryMajorEnroll
err := config.DB.Where("year = ?", year).Find(&items).Error
return items, err
}
func (m *YxHistoryMajorEnrollMapper) BatchCreate(items []entity.YxHistoryMajorEnroll, batchSize int) error {
return config.DB.CreateInBatches(items, batchSize).Error
}
func (m *YxHistoryMajorEnrollMapper) BatchUpdate(items []entity.YxHistoryMajorEnroll) error {
return config.DB.Save(items).Error
}
func (m *YxHistoryMajorEnrollMapper) BatchUpsert(items []entity.YxHistoryMajorEnroll, updateColumns []string) error {
return config.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns(updateColumns),
}).CreateInBatches(items, 100).Error
}
func (m *YxHistoryMajorEnrollMapper) BatchDelete(ids []string) error {
return config.DB.Delete(&entity.YxHistoryMajorEnroll{}, "id IN ?", ids).Error
}

View File

@ -0,0 +1,64 @@
// Package mapper 数据访问层
package mapper
import (
"server/config"
"server/modules/yx/entity"
"gorm.io/gorm/clause"
)
type YxSchoolMajorMapper struct{}
func NewYxSchoolMajorMapper() *YxSchoolMajorMapper {
return &YxSchoolMajorMapper{}
}
func (m *YxSchoolMajorMapper) FindAll(page, size int) ([]entity.YxSchoolMajor, int64, error) {
var items []entity.YxSchoolMajor
var total int64
config.DB.Model(&entity.YxSchoolMajor{}).Count(&total)
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
return items, total, err
}
func (m *YxSchoolMajorMapper) FindByID(id string) (*entity.YxSchoolMajor, error) {
var item entity.YxSchoolMajor
err := config.DB.First(&item, "id = ?", id).Error
return &item, err
}
func (m *YxSchoolMajorMapper) Create(item *entity.YxSchoolMajor) error {
return config.DB.Create(item).Error
}
func (m *YxSchoolMajorMapper) Update(item *entity.YxSchoolMajor) error {
return config.DB.Save(item).Error
}
func (m *YxSchoolMajorMapper) UpdateFields(id string, fields map[string]interface{}) error {
return config.DB.Model(&entity.YxSchoolMajor{}).Where("id = ?", id).Updates(fields).Error
}
func (m *YxSchoolMajorMapper) Delete(id string) error {
return config.DB.Delete(&entity.YxSchoolMajor{}, "id = ?", id).Error
}
func (m *YxSchoolMajorMapper) BatchCreate(items []entity.YxSchoolMajor, batchSize int) error {
return config.DB.CreateInBatches(items, batchSize).Error
}
func (m *YxSchoolMajorMapper) BatchUpdate(items []entity.YxSchoolMajor) error {
return config.DB.Save(items).Error
}
func (m *YxSchoolMajorMapper) BatchUpsert(items []entity.YxSchoolMajor, updateColumns []string) error {
return config.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns(updateColumns),
}).CreateInBatches(items, 100).Error
}
func (m *YxSchoolMajorMapper) BatchDelete(ids []string) error {
return config.DB.Delete(&entity.YxSchoolMajor{}, "id IN ?", ids).Error
}

View File

@ -0,0 +1,74 @@
// Package service 业务逻辑层
package service
import (
"server/modules/yx/entity"
"server/modules/yx/mapper"
"github.com/google/uuid"
)
type YxCalculationMajorService struct {
mapper *mapper.YxCalculationMajorMapper
}
func NewYxCalculationMajorService() *YxCalculationMajorService {
return &YxCalculationMajorService{mapper: mapper.NewYxCalculationMajorMapper()}
}
func (s *YxCalculationMajorService) List(page, size int) ([]entity.YxCalculationMajor, int64, error) {
return s.mapper.FindAll(page, size)
}
func (s *YxCalculationMajorService) GetByID(id string) (*entity.YxCalculationMajor, error) {
return s.mapper.FindByID(id)
}
func (s *YxCalculationMajorService) Create(item *entity.YxCalculationMajor) error {
item.ID = uuid.New().String()
return s.mapper.Create(item)
}
func (s *YxCalculationMajorService) Update(item *entity.YxCalculationMajor) error {
return s.mapper.Update(item)
}
func (s *YxCalculationMajorService) UpdateFields(id string, fields map[string]interface{}) error {
return s.mapper.UpdateFields(id, fields)
}
func (s *YxCalculationMajorService) Delete(id string) error {
return s.mapper.Delete(id)
}
func (s *YxCalculationMajorService) GetByScoreID(scoreID string) ([]entity.YxCalculationMajor, error) {
return s.mapper.FindByScoreID(scoreID)
}
func (s *YxCalculationMajorService) BatchCreate(items []entity.YxCalculationMajor) error {
for i := range items {
items[i].ID = uuid.New().String()
}
return s.mapper.BatchCreate(items, 100)
}
func (s *YxCalculationMajorService) BatchUpdate(items []entity.YxCalculationMajor) error {
return s.mapper.BatchUpdate(items)
}
func (s *YxCalculationMajorService) BatchUpsert(items []entity.YxCalculationMajor, updateColumns []string) error {
for i := range items {
if items[i].ID == "" {
items[i].ID = uuid.New().String()
}
}
return s.mapper.BatchUpsert(items, updateColumns)
}
func (s *YxCalculationMajorService) BatchDelete(ids []string) error {
return s.mapper.BatchDelete(ids)
}
func (s *YxCalculationMajorService) DeleteByScoreID(scoreID string) error {
return s.mapper.DeleteByScoreID(scoreID)
}

View File

@ -0,0 +1,70 @@
// Package service 业务逻辑层
package service
import (
"server/modules/yx/entity"
"server/modules/yx/mapper"
"github.com/google/uuid"
)
type YxHistoryMajorEnrollService struct {
mapper *mapper.YxHistoryMajorEnrollMapper
}
func NewYxHistoryMajorEnrollService() *YxHistoryMajorEnrollService {
return &YxHistoryMajorEnrollService{mapper: mapper.NewYxHistoryMajorEnrollMapper()}
}
func (s *YxHistoryMajorEnrollService) List(page, size int) ([]entity.YxHistoryMajorEnroll, int64, error) {
return s.mapper.FindAll(page, size)
}
func (s *YxHistoryMajorEnrollService) GetByID(id string) (*entity.YxHistoryMajorEnroll, error) {
return s.mapper.FindByID(id)
}
func (s *YxHistoryMajorEnrollService) Create(item *entity.YxHistoryMajorEnroll) error {
item.ID = uuid.New().String()
return s.mapper.Create(item)
}
func (s *YxHistoryMajorEnrollService) Update(item *entity.YxHistoryMajorEnroll) error {
return s.mapper.Update(item)
}
func (s *YxHistoryMajorEnrollService) UpdateFields(id string, fields map[string]interface{}) error {
return s.mapper.UpdateFields(id, fields)
}
func (s *YxHistoryMajorEnrollService) Delete(id string) error {
return s.mapper.Delete(id)
}
func (s *YxHistoryMajorEnrollService) GetByYear(year string) ([]entity.YxHistoryMajorEnroll, error) {
return s.mapper.FindByYear(year)
}
func (s *YxHistoryMajorEnrollService) BatchCreate(items []entity.YxHistoryMajorEnroll) error {
for i := range items {
items[i].ID = uuid.New().String()
}
return s.mapper.BatchCreate(items, 100)
}
func (s *YxHistoryMajorEnrollService) BatchUpdate(items []entity.YxHistoryMajorEnroll) error {
return s.mapper.BatchUpdate(items)
}
func (s *YxHistoryMajorEnrollService) BatchUpsert(items []entity.YxHistoryMajorEnroll, updateColumns []string) error {
for i := range items {
if items[i].ID == "" {
items[i].ID = uuid.New().String()
}
}
return s.mapper.BatchUpsert(items, updateColumns)
}
func (s *YxHistoryMajorEnrollService) BatchDelete(ids []string) error {
return s.mapper.BatchDelete(ids)
}

View File

@ -0,0 +1,66 @@
// Package service 业务逻辑层
package service
import (
"server/modules/yx/entity"
"server/modules/yx/mapper"
"github.com/google/uuid"
)
type YxSchoolMajorService struct {
mapper *mapper.YxSchoolMajorMapper
}
func NewYxSchoolMajorService() *YxSchoolMajorService {
return &YxSchoolMajorService{mapper: mapper.NewYxSchoolMajorMapper()}
}
func (s *YxSchoolMajorService) List(page, size int) ([]entity.YxSchoolMajor, int64, error) {
return s.mapper.FindAll(page, size)
}
func (s *YxSchoolMajorService) GetByID(id string) (*entity.YxSchoolMajor, error) {
return s.mapper.FindByID(id)
}
func (s *YxSchoolMajorService) Create(item *entity.YxSchoolMajor) error {
item.ID = uuid.New().String()
return s.mapper.Create(item)
}
func (s *YxSchoolMajorService) Update(item *entity.YxSchoolMajor) error {
return s.mapper.Update(item)
}
func (s *YxSchoolMajorService) UpdateFields(id string, fields map[string]interface{}) error {
return s.mapper.UpdateFields(id, fields)
}
func (s *YxSchoolMajorService) Delete(id string) error {
return s.mapper.Delete(id)
}
func (s *YxSchoolMajorService) BatchCreate(items []entity.YxSchoolMajor) error {
for i := range items {
items[i].ID = uuid.New().String()
}
return s.mapper.BatchCreate(items, 100)
}
func (s *YxSchoolMajorService) BatchUpdate(items []entity.YxSchoolMajor) error {
return s.mapper.BatchUpdate(items)
}
func (s *YxSchoolMajorService) BatchUpsert(items []entity.YxSchoolMajor, updateColumns []string) error {
for i := range items {
if items[i].ID == "" {
items[i].ID = uuid.New().String()
}
}
return s.mapper.BatchUpsert(items, updateColumns)
}
func (s *YxSchoolMajorService) BatchDelete(ids []string) error {
return s.mapper.BatchDelete(ids)
}