初始化代码
This commit is contained in:
commit
3f61311678
|
|
@ -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": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"kiroAgent.configureMCP": "Disabled"
|
||||||
|
}
|
||||||
|
|
@ -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"})
|
||||||
|
```
|
||||||
|
|
@ -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。
|
||||||
|
|
@ -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过滤必须登录鉴权才可访问),在接口代码中可以获取当前用户的登录信息。
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
*.go linguist-language=Go
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
## ide
|
||||||
|
**/.idea
|
||||||
|
*.iml
|
||||||
|
rebel.xml
|
||||||
|
|
||||||
|
## backend
|
||||||
|
**/target
|
||||||
|
**/logs
|
||||||
|
|
||||||
|
## front
|
||||||
|
**/*.lock
|
||||||
|
|
@ -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 # 新的一天
|
||||||
|
```
|
||||||
|
|
@ -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 ""
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
@ -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(">> 测试失败!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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})
|
||||||
|
}
|
||||||
|
|
@ -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 // 最大请求次数
|
||||||
|
}
|
||||||
|
|
@ -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("数据库连接已关闭")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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连接已关闭")
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -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"
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
@ -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=
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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...)
|
||||||
|
}
|
||||||
|
|
@ -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...)
|
||||||
|
}
|
||||||
|
|
@ -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...)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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"`
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue