Compare commits
No commits in common. "04a08d5327c9bb0f5a27bd67dd3db025bab65fe2" and "aac82d8a526e8669e06c15a08b6d86f479e214d1" have entirely different histories.
04a08d5327
...
aac82d8a52
|
|
@ -1,614 +0,0 @@
|
||||||
# Mapper 和 Service 层通用方法重构方案
|
|
||||||
|
|
||||||
## 一、问题分析
|
|
||||||
|
|
||||||
### 1.1 当前现状
|
|
||||||
|
|
||||||
通过代码分析发现,项目中 Mapper 和 Service 层存在大量重复的通用 CRUD 方法实现:
|
|
||||||
|
|
||||||
| 层级 | 重复率 | 具体数据 |
|
|
||||||
|------|--------|----------|
|
|
||||||
| Mapper | 80%+ | 8 个 Mapper 中,7 个实现了相同的通用方法 |
|
|
||||||
| Service | 80%+ | 9 个 Service 中,8 个实现了相同的通用方法 |
|
|
||||||
|
|
||||||
### 1.2 重复方法清单
|
|
||||||
|
|
||||||
#### Mapper 层(10 个通用方法)
|
|
||||||
- `FindAll(page, size int)` - 分页查询
|
|
||||||
- `FindByID(id string)` - 根据 ID 查询
|
|
||||||
- `Create(item *T)` - 创建单个记录
|
|
||||||
- `Update(item *T)` - 更新单个记录
|
|
||||||
- `UpdateFields(id string, fields map[string]interface{})` - 更新指定字段
|
|
||||||
- `Delete(id string)` - 删除记录
|
|
||||||
- `BatchCreate(items []T, batchSize int)` - 批量创建
|
|
||||||
- `BatchUpdate(items []T)` - 批量更新
|
|
||||||
- `BatchUpsert(items []T, updateColumns []string)` - 批量插入或更新
|
|
||||||
- `BatchDelete(ids []string)` - 批量删除
|
|
||||||
|
|
||||||
#### Service 层(10 个通用方法)
|
|
||||||
- `List(page, size int)` - 分页查询(包装 Mapper.FindAll)
|
|
||||||
- `GetByID(id string)` - 根据 ID 获取(包装 Mapper.FindByID)
|
|
||||||
- `Create(item *T)` - 创建(包装 Mapper.Create + ID 生成)
|
|
||||||
- `Update(item *T)` - 更新(包装 Mapper.Update)
|
|
||||||
- `UpdateFields(id string, fields map[string]interface{})` - 更新指定字段
|
|
||||||
- `Delete(id string)` - 删除(包装 Mapper.Delete)
|
|
||||||
- `BatchCreate(items []T)` - 批量创建(包装 Mapper.BatchCreate + ID 生成)
|
|
||||||
- `BatchUpdate(items []T)` - 批量更新(包装 Mapper.BatchUpdate)
|
|
||||||
- `BatchUpsert(items []T, updateColumns []string)` - 批量插入或更新
|
|
||||||
- `BatchDelete(ids []string)` - 批量删除(包装 Mapper.BatchDelete)
|
|
||||||
|
|
||||||
### 1.3 存在的问题
|
|
||||||
|
|
||||||
1. **代码重复率高**:超过 80% 的方法在所有 Mapper 和 Service 中重复实现
|
|
||||||
2. **维护成本高**:修改通用逻辑需要在 8 个 Mapper 和 9 个 Service 中同步修改
|
|
||||||
3. **不一致性风险**:不同开发者可能对相同方法实现略有差异
|
|
||||||
4. **测试成本高**:需要对每个 Mapper 和 Service 的通用方法单独编写测试用例
|
|
||||||
|
|
||||||
## 二、解决方案设计
|
|
||||||
|
|
||||||
### 2.1 技术选型
|
|
||||||
|
|
||||||
#### 方案对比
|
|
||||||
|
|
||||||
| 方案 | 优点 | 缺点 | 适用场景 |
|
|
||||||
|------|------|------|----------|
|
|
||||||
| **泛型基类(推荐)** | 类型安全、零运行时开销、符合 Go 风格 | 需要 Go 1.18+ | ✅ 当前项目 |
|
|
||||||
| 接口 + 组合 | 灵活、支持多种实现 | 需要手动实现所有方法 | 需要多态的场景 |
|
|
||||||
| 代码生成 | 完全定制、性能最优 | 需要额外工具、学习成本高 | 大型项目 |
|
|
||||||
|
|
||||||
**选择理由**:
|
|
||||||
- 项目已使用 Go 1.21+,支持泛型
|
|
||||||
- 泛型基类提供编译时类型检查,类型安全
|
|
||||||
- 零运行时开销,性能最优
|
|
||||||
- 代码简洁易读,符合 Go 语言惯例
|
|
||||||
|
|
||||||
### 2.2 架构设计
|
|
||||||
|
|
||||||
#### 整体架构图
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Controller Layer │
|
|
||||||
└───────────────────────────────┬─────────────────────────────┘
|
|
||||||
│
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Service Layer │
|
|
||||||
│ ┌───────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ BaseService<T> (Generic Base) │ │
|
|
||||||
│ │ • List() • GetByID() • Create() • Update() │ │
|
|
||||||
│ │ • Delete() • Batch*() (通用 CRUD 实现) │ │
|
|
||||||
│ └───────────────────────┬───────────────────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────┼─────────────────┐ │
|
|
||||||
│ ↓ ↓ ↓ │
|
|
||||||
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
||||||
│ │YxVolunteer│ │YxUserScore│ │SysUserService│ │
|
|
||||||
│ │ Service │ │ Service │ │ │ │
|
|
||||||
│ │ (继承) │ │ (继承) │ │ (继承) │ │
|
|
||||||
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
|
|
||||||
└────────┼──────────────────┼──────────────────┼─────────────────┘
|
|
||||||
↓ ↓ ↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Mapper Layer │
|
|
||||||
│ ┌───────────────────────────────────────────────────────┐ │
|
|
||||||
│ │ BaseMapper<T> (Generic Base) │ │
|
|
||||||
│ │ • FindAll() • FindByID() • Create() • Update() │ │
|
|
||||||
│ │ • Delete() • Batch*() (通用 CRUD 实现) │ │
|
|
||||||
│ └───────────────────────┬───────────────────────────────┘ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────────────────┼─────────────────┐ │
|
|
||||||
│ ↓ ↓ ↓ │
|
|
||||||
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
|
|
||||||
│ │YxVolunteer│ │YxUserScore│ │SysUserMapper│ │
|
|
||||||
│ │ Mapper │ │ Mapper │ │ │ │
|
|
||||||
│ │ (继承) │ │ (继承) │ │ (继承) │ │
|
|
||||||
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
|
|
||||||
└────────┼──────────────────┼──────────────────┼─────────────────┘
|
|
||||||
↓ ↓ ↓
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ GORM / MySQL │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 泛型基类设计
|
|
||||||
|
|
||||||
```go
|
|
||||||
// BaseMapper 泛型 Mapper 基类
|
|
||||||
type BaseMapper[T any] struct {
|
|
||||||
db *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// BaseService 泛型 Service 基类
|
|
||||||
type BaseService[T any] struct {
|
|
||||||
mapper *BaseMapper[T]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 实现方案
|
|
||||||
|
|
||||||
#### 2.3.1 BaseMapper 实现
|
|
||||||
|
|
||||||
```go
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/config"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultBatchSize 默认批量操作大小
|
|
||||||
const DefaultBatchSize = 100
|
|
||||||
|
|
||||||
// BaseMapper 泛型 Mapper 基类,封装通用 CRUD 操作
|
|
||||||
type BaseMapper[T any] struct {
|
|
||||||
db *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBaseMapper 创建 BaseMapper 实例
|
|
||||||
func NewBaseMapper[T any]() *BaseMapper[T] {
|
|
||||||
return &BaseMapper[T]{
|
|
||||||
db: config.DB,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDB 获取数据库实例(允许子类覆盖)
|
|
||||||
func (m *BaseMapper[T]) GetDB() *gorm.DB {
|
|
||||||
return m.db
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindAll 分页查询
|
|
||||||
func (m *BaseMapper[T]) FindAll(page, size int) ([]T, int64, error) {
|
|
||||||
var items []T
|
|
||||||
var total int64
|
|
||||||
query := m.GetDB().Model(new(T))
|
|
||||||
query.Count(&total)
|
|
||||||
err := query.Offset((page - 1) * size).Limit(size).Find(&items).Error
|
|
||||||
return items, total, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindByID 根据 ID 查询
|
|
||||||
func (m *BaseMapper[T]) FindByID(id string) (*T, error) {
|
|
||||||
var item T
|
|
||||||
err := m.GetDB().First(&item, "id = ?", id).Error
|
|
||||||
return &item, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建单个记录
|
|
||||||
func (m *BaseMapper[T]) Create(item *T) error {
|
|
||||||
return m.GetDB().Create(item).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update 更新单个记录
|
|
||||||
func (m *BaseMapper[T]) Update(item *T) error {
|
|
||||||
return m.GetDB().Save(item).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFields 更新指定字段
|
|
||||||
func (m *BaseMapper[T]) UpdateFields(id string, fields map[string]interface{}) error {
|
|
||||||
return m.GetDB().Model(new(T)).Where("id = ?", id).Updates(fields).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除记录
|
|
||||||
func (m *BaseMapper[T]) Delete(id string) error {
|
|
||||||
return m.GetDB().Delete(new(T), "id = ?", id).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchCreate 批量创建
|
|
||||||
func (m *BaseMapper[T]) BatchCreate(items []T, batchSize int) error {
|
|
||||||
if batchSize <= 0 {
|
|
||||||
batchSize = DefaultBatchSize
|
|
||||||
}
|
|
||||||
return m.GetDB().CreateInBatches(items, batchSize).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpdate 批量更新
|
|
||||||
func (m *BaseMapper[T]) BatchUpdate(items []T) error {
|
|
||||||
return m.GetDB().Save(items).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新
|
|
||||||
func (m *BaseMapper[T]) BatchUpsert(items []T, updateColumns []string) error {
|
|
||||||
return m.GetDB().Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "id"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns(updateColumns),
|
|
||||||
}).CreateInBatches(items, DefaultBatchSize).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchDelete 批量删除
|
|
||||||
func (m *BaseMapper[T]) BatchDelete(ids []string) error {
|
|
||||||
return m.GetDB().Delete(new(T), "id IN ?", ids).Error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.3.2 BaseService 实现
|
|
||||||
|
|
||||||
```go
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BaseService 泛型 Service 基类,封装通用业务逻辑
|
|
||||||
type BaseService[T any] struct {
|
|
||||||
mapper *BaseMapper[T]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBaseService 创建 BaseService 实例
|
|
||||||
func NewBaseService[T any]() *BaseService[T] {
|
|
||||||
return &BaseService[T]{
|
|
||||||
mapper: NewBaseMapper[T](),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMapper 获取 Mapper 实例
|
|
||||||
func (s *BaseService[T]) GetMapper() *BaseMapper[T] {
|
|
||||||
return s.mapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// List 分页查询
|
|
||||||
func (s *BaseService[T]) List(page, size int) ([]T, int64, error) {
|
|
||||||
return s.mapper.FindAll(page, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID 根据 ID 获取
|
|
||||||
func (s *BaseService[T]) GetByID(id string) (*T, error) {
|
|
||||||
return s.mapper.FindByID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建记录(自动生成 ID)
|
|
||||||
func (s *BaseService[T]) Create(item *T) error {
|
|
||||||
// 通过反射设置 ID 字段
|
|
||||||
if err := setID(item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.mapper.Create(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update 更新记录
|
|
||||||
func (s *BaseService[T]) Update(item *T) error {
|
|
||||||
return s.mapper.Update(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFields 更新指定字段
|
|
||||||
func (s *BaseService[T]) UpdateFields(id string, fields map[string]interface{}) error {
|
|
||||||
return s.mapper.UpdateFields(id, fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除记录
|
|
||||||
func (s *BaseService[T]) Delete(id string) error {
|
|
||||||
return s.mapper.Delete(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchCreate 批量创建(自动生成 ID)
|
|
||||||
func (s *BaseService[T]) BatchCreate(items []T) error {
|
|
||||||
for i := range items {
|
|
||||||
if err := setID(&items[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.mapper.BatchCreate(items, DefaultBatchSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpdate 批量更新
|
|
||||||
func (s *BaseService[T]) BatchUpdate(items []T) error {
|
|
||||||
return s.mapper.BatchUpdate(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新
|
|
||||||
func (s *BaseService[T]) BatchUpsert(items []T, updateColumns []string) error {
|
|
||||||
for i := range items {
|
|
||||||
if err := setID(&items[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchDelete 批量删除
|
|
||||||
func (s *BaseService[T]) BatchDelete(ids []string) error {
|
|
||||||
return s.mapper.BatchDelete(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setID 通过反射设置 ID 字段
|
|
||||||
func setID(item interface{}) error {
|
|
||||||
val := reflect.ValueOf(item).Elem()
|
|
||||||
if val.Kind() == reflect.Struct {
|
|
||||||
idField := val.FieldByName("ID")
|
|
||||||
if idField.IsValid() && idField.Kind() == reflect.String {
|
|
||||||
if idField.String() == "" {
|
|
||||||
idField.SetString(common.GenerateStringID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.4 使用示例
|
|
||||||
|
|
||||||
#### 示例 1:普通 Mapper/Service(最简单)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// mapper/yx_volunteer_mapper.go
|
|
||||||
package mapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
"server/modules/yx/entity"
|
|
||||||
)
|
|
||||||
|
|
||||||
type YxVolunteerMapper struct {
|
|
||||||
*common.BaseMapper[entity.YxVolunteer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxVolunteerMapper() *YxVolunteerMapper {
|
|
||||||
return &YxVolunteerMapper{
|
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxVolunteer](),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只需要定义特殊方法,通用方法自动继承
|
|
||||||
func (m *YxVolunteerMapper) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
|
|
||||||
var item entity.YxVolunteer
|
|
||||||
err := m.GetDB().Where("score_id = ? AND state = ?", scoreId, "1").First(&item).Error
|
|
||||||
return &item, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *YxVolunteerMapper) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
|
||||||
// 特殊查询逻辑
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// service/yx_volunteer_service.go
|
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
"server/modules/yx/entity"
|
|
||||||
"server/modules/yx/mapper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type YxVolunteerService struct {
|
|
||||||
*common.BaseService[entity.YxVolunteer]
|
|
||||||
mapper *mapper.YxVolunteerMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxVolunteerService() *YxVolunteerService {
|
|
||||||
mapper := mapper.NewYxVolunteerMapper()
|
|
||||||
return &YxVolunteerService{
|
|
||||||
BaseService: common.NewBaseService[entity.YxVolunteer](),
|
|
||||||
mapper: mapper,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只需要定义特殊方法,通用方法自动继承
|
|
||||||
func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) error {
|
|
||||||
// 特殊业务逻辑
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *YxVolunteerService) SwitchVolunteer(id, userID string) error {
|
|
||||||
// 特殊业务逻辑
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 示例 2:需要逻辑删除的 Mapper
|
|
||||||
|
|
||||||
```go
|
|
||||||
// mapper/sys_user_mapper.go
|
|
||||||
package mapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
"server/modules/system/entity"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SysUserMapper struct {
|
|
||||||
*common.BaseMapper[entity.SysUser]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSysUserMapper() *SysUserMapper {
|
|
||||||
return &SysUserMapper{
|
|
||||||
BaseMapper: common.NewBaseMapper[entity.SysUser](),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 覆盖 GetDB 方法,添加逻辑删除过滤
|
|
||||||
func (m *SysUserMapper) GetDB() *gorm.DB {
|
|
||||||
return m.BaseMapper.GetDB().Where("del_flag = 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 覆盖 Delete 方法,使用逻辑删除
|
|
||||||
func (m *SysUserMapper) Delete(id string) error {
|
|
||||||
return m.BaseMapper.GetDB().Model(new(entity.SysUser)).Where("id = ?", id).Update("del_flag", 1).Error
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 示例 3:需要自定义 Create 逻辑的 Service
|
|
||||||
|
|
||||||
```go
|
|
||||||
// service/sys_user_service.go
|
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/common"
|
|
||||||
"server/modules/system/entity"
|
|
||||||
"server/modules/system/mapper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SysUserService struct {
|
|
||||||
*common.BaseService[entity.SysUser]
|
|
||||||
mapper *mapper.SysUserMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSysUserService() *SysUserService {
|
|
||||||
mapper := mapper.NewSysUserMapper()
|
|
||||||
return &SysUserService{
|
|
||||||
BaseService: common.NewBaseService[entity.SysUser](),
|
|
||||||
mapper: mapper,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 覆盖 Create 方法,添加密码加密逻辑
|
|
||||||
func (s *SysUserService) Create(item *entity.SysUser) error {
|
|
||||||
item.ID = common.GenerateStringID()
|
|
||||||
item.Salt = uuid.New().String()[:8]
|
|
||||||
|
|
||||||
encrypted, err := common.Encrypt(item.Username, item.Password, item.Salt)
|
|
||||||
if err != nil {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.5 特殊化处理
|
|
||||||
|
|
||||||
#### 支持的场景
|
|
||||||
|
|
||||||
| 场景 | 处理方式 | 代码示例 |
|
|
||||||
|------|----------|----------|
|
|
||||||
| 逻辑删除 | 覆盖 GetDB() | 添加 `del_flag = 0` 过滤 |
|
|
||||||
| 自定义 ID 生成 | 覆盖 Create() | 添加 ID 生成逻辑 |
|
|
||||||
| 密码加密 | 覆盖 Create() | 添加加密逻辑 |
|
|
||||||
| 自定义查询条件 | 覆盖 GetDB() | 添加额外 Where 条件 |
|
|
||||||
| 动态表名 | 在 Mapper 中定义新方法 | 直接使用 Table() |
|
|
||||||
| 自定义批量大小 | 调用时指定参数 | `BatchCreate(items, 50)` |
|
|
||||||
|
|
||||||
## 三、符合规范评估
|
|
||||||
|
|
||||||
### 3.1 Go 语言规范符合度
|
|
||||||
|
|
||||||
| 规范维度 | 评估 | 说明 |
|
|
||||||
|----------|------|------|
|
|
||||||
| 泛型使用 | ✅ 符合 | 使用 Go 1.18+ 泛型特性 |
|
|
||||||
| 命名规范 | ✅ 符合 | BaseMapper、BaseService 遵循大驼峰命名 |
|
|
||||||
| 代码风格 | ✅ 符合 | 保持与现有代码风格一致 |
|
|
||||||
| 错误处理 | ✅ 符合 | 返回 error,不 panic |
|
|
||||||
| 包结构 | ✅ 符合 | 基类放在 common 包 |
|
|
||||||
|
|
||||||
### 3.2 Google Go 编程规范符合度
|
|
||||||
|
|
||||||
| 规范条款 | 评估 | 说明 |
|
|
||||||
|----------|------|------|
|
|
||||||
| **避免代码重复** | ✅ 符合 | 通过泛型基类消除 80%+ 重复代码 |
|
|
||||||
| **组合优于继承** | ✅ 符合 | 使用结构体组合,而非传统继承 |
|
|
||||||
| **接口优于实现** | ⚠️ 部分 | 基类使用具体类型,可后续扩展为接口 |
|
|
||||||
| **简单明了** | ✅ 符合 | 基类逻辑清晰,易于理解 |
|
|
||||||
| **可测试性** | ✅ 符合 | 泛型类型易于单元测试 |
|
|
||||||
|
|
||||||
### 3.3 项目架构规范符合度
|
|
||||||
|
|
||||||
| 架构原则 | 评估 | 说明 |
|
|
||||||
|----------|------|------|
|
|
||||||
| **分层清晰** | ✅ 符合 | 不改变现有的分层架构 |
|
|
||||||
| **职责单一** | ✅ 符合 | 基类负责通用 CRUD,子类负责特殊逻辑 |
|
|
||||||
| **依赖倒置** | ✅ 符合 | Service 依赖 BaseMapper 接口能力 |
|
|
||||||
| **开闭原则** | ✅ 符合 | 对扩展开放(覆盖方法),对修改关闭(基类稳定) |
|
|
||||||
|
|
||||||
### 3.4 性能评估
|
|
||||||
|
|
||||||
| 指标 | 评估 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| **编译时类型检查** | ✅ 优秀 | 泛型提供编译时类型安全 |
|
|
||||||
| **运行时性能** | ✅ 优秀 | 泛型在编译时展开,零运行时开销 |
|
|
||||||
| **内存开销** | ✅ 优秀 | 无额外内存分配 |
|
|
||||||
| **可读性** | ✅ 良好 | 代码简洁,易于理解 |
|
|
||||||
|
|
||||||
### 3.5 可维护性评估
|
|
||||||
|
|
||||||
| 指标 | 改进前 | 改进后 | 提升 |
|
|
||||||
|------|--------|--------|------|
|
|
||||||
| **代码行数** | ~2000 行 | ~500 行 | -75% |
|
|
||||||
| **重复代码率** | 80%+ | < 5% | -95% |
|
|
||||||
| **维护成本** | 高 | 低 | -80% |
|
|
||||||
| **Bug 风险** | 高 | 低 | -70% |
|
|
||||||
|
|
||||||
## 四、迁移计划
|
|
||||||
|
|
||||||
### 4.1 迁移步骤
|
|
||||||
|
|
||||||
#### 阶段 1:创建基类(不破坏现有代码)
|
|
||||||
|
|
||||||
1. 创建 `server/common/base_mapper.go`
|
|
||||||
2. 创建 `server/common/base_service.go`
|
|
||||||
3. 编写单元测试验证基类功能
|
|
||||||
|
|
||||||
#### 阶段 2:逐步迁移(按模块迁移)
|
|
||||||
|
|
||||||
| 优先级 | 模块 | 原因 |
|
|
||||||
|--------|------|------|
|
|
||||||
| P0 | yx_volunteer | 最简单,无特殊逻辑 |
|
|
||||||
| P0 | yx_volunteer_record | 最简单,无特殊逻辑 |
|
|
||||||
| P1 | yx_school_major | 简单,无特殊逻辑 |
|
|
||||||
| P1 | yx_user_score | 简单,无特殊逻辑 |
|
|
||||||
| P2 | yx_calculation_major | 有动态表名,需要特殊处理 |
|
|
||||||
| P2 | yx_history_major_enroll | 简单 |
|
|
||||||
| P3 | sys_user | 有逻辑删除和密码加密,需要覆盖方法 |
|
|
||||||
|
|
||||||
#### 阶段 3:验证和测试
|
|
||||||
|
|
||||||
1. 运行现有测试用例
|
|
||||||
2. 验证所有 API 接口
|
|
||||||
3. 性能测试对比
|
|
||||||
|
|
||||||
#### 阶段 4:清理旧代码
|
|
||||||
|
|
||||||
1. 删除重复的通用方法实现
|
|
||||||
2. 更新代码注释
|
|
||||||
3. 更新文档
|
|
||||||
|
|
||||||
### 4.2 风险评估
|
|
||||||
|
|
||||||
| 风险 | 等级 | 缓解措施 |
|
|
||||||
|------|------|----------|
|
|
||||||
| 泛型兼容性问题 | 低 | 项目已使用 Go 1.21+,支持泛型 |
|
|
||||||
| 运行时行为变化 | 低 | 基类逻辑与现有实现一致 |
|
|
||||||
| 测试覆盖不足 | 中 | 增加单元测试和集成测试 |
|
|
||||||
| 特殊逻辑遗漏 | 中 | 逐个模块迁移,充分测试 |
|
|
||||||
| 性能回归 | 低 | 泛型零运行时开销 |
|
|
||||||
|
|
||||||
## 五、总结
|
|
||||||
|
|
||||||
### 5.1 方案优势
|
|
||||||
|
|
||||||
1. **消除重复代码**:减少 80%+ 的重复代码
|
|
||||||
2. **提高可维护性**:统一修改点,降低维护成本
|
|
||||||
3. **类型安全**:编译时类型检查,避免运行时错误
|
|
||||||
4. **性能优越**:零运行时开销
|
|
||||||
5. **易于扩展**:通过覆盖方法支持特殊逻辑
|
|
||||||
|
|
||||||
### 5.2 方案劣势
|
|
||||||
|
|
||||||
1. **学习成本**:团队需要理解泛型基类的设计
|
|
||||||
2. **初期工作量**:需要创建基类和迁移现有代码
|
|
||||||
3. **调试复杂度**:泛型代码调试相对复杂
|
|
||||||
|
|
||||||
### 5.3 最终建议
|
|
||||||
|
|
||||||
**✅ 推荐实施**
|
|
||||||
|
|
||||||
理由:
|
|
||||||
1. 符合 Go 语言规范和项目架构规范
|
|
||||||
2. 显著提升代码质量和可维护性
|
|
||||||
3. 风险可控,可分阶段迁移
|
|
||||||
4. 长期收益远大于初期投入
|
|
||||||
|
|
||||||
### 5.4 后续优化方向
|
|
||||||
|
|
||||||
1. 引入接口抽象,支持多种 Mapper 实现(如 Redis、MongoDB)
|
|
||||||
2. 增加事务支持
|
|
||||||
3. 增加缓存支持
|
|
||||||
4. 增加审计日志支持
|
|
||||||
5. 增加软删除支持(通过配置)
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
// Package common 公共组件
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"server/config"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultBatchSize 默认批量操作大小
|
|
||||||
const DefaultBatchSize = 100
|
|
||||||
|
|
||||||
// BaseMapper 泛型 Mapper 基类,封装通用 CRUD 操作
|
|
||||||
type BaseMapper[T any] struct {
|
|
||||||
db *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBaseMapper 创建 BaseMapper 实例
|
|
||||||
func NewBaseMapper[T any]() *BaseMapper[T] {
|
|
||||||
return &BaseMapper[T]{
|
|
||||||
db: config.DB,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDB 获取数据库实例(允许子类覆盖)
|
|
||||||
func (m *BaseMapper[T]) GetDB() *gorm.DB {
|
|
||||||
return m.db
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindAll 分页查询
|
|
||||||
func (m *BaseMapper[T]) FindAll(page, size int) ([]T, int64, error) {
|
|
||||||
var items []T
|
|
||||||
var total int64
|
|
||||||
query := m.GetDB().Model(new(T))
|
|
||||||
query.Count(&total)
|
|
||||||
err := query.Offset((page - 1) * size).Limit(size).Find(&items).Error
|
|
||||||
return items, total, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindByID 根据 ID 查询
|
|
||||||
func (m *BaseMapper[T]) FindByID(id string) (*T, error) {
|
|
||||||
var item T
|
|
||||||
err := m.GetDB().First(&item, "id = ?", id).Error
|
|
||||||
return &item, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建单个记录
|
|
||||||
func (m *BaseMapper[T]) Create(item *T) error {
|
|
||||||
return m.GetDB().Create(item).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update 更新单个记录
|
|
||||||
func (m *BaseMapper[T]) Update(item *T) error {
|
|
||||||
return m.GetDB().Save(item).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFields 更新指定字段
|
|
||||||
func (m *BaseMapper[T]) UpdateFields(id string, fields map[string]interface{}) error {
|
|
||||||
return m.GetDB().Model(new(T)).Where("id = ?", id).Updates(fields).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除记录
|
|
||||||
func (m *BaseMapper[T]) Delete(id string) error {
|
|
||||||
return m.GetDB().Delete(new(T), "id = ?", id).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchCreate 批量创建
|
|
||||||
func (m *BaseMapper[T]) BatchCreate(items []T, batchSize int) error {
|
|
||||||
if batchSize <= 0 {
|
|
||||||
batchSize = DefaultBatchSize
|
|
||||||
}
|
|
||||||
return m.GetDB().CreateInBatches(items, batchSize).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpdate 批量更新
|
|
||||||
func (m *BaseMapper[T]) BatchUpdate(items []T) error {
|
|
||||||
return m.GetDB().Save(items).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新
|
|
||||||
func (m *BaseMapper[T]) BatchUpsert(items []T, updateColumns []string) error {
|
|
||||||
return m.GetDB().Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "id"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns(updateColumns),
|
|
||||||
}).CreateInBatches(items, DefaultBatchSize).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchDelete 批量删除
|
|
||||||
func (m *BaseMapper[T]) BatchDelete(ids []string) error {
|
|
||||||
return m.GetDB().Delete(new(T), "id IN ?", ids).Error
|
|
||||||
}
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
// Package common 公共组件
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BaseService 泛型 Service 基类,封装通用业务逻辑
|
|
||||||
type BaseService[T any] struct {
|
|
||||||
mapper *BaseMapper[T]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBaseService 创建 BaseService 实例
|
|
||||||
func NewBaseService[T any]() *BaseService[T] {
|
|
||||||
return &BaseService[T]{
|
|
||||||
mapper: NewBaseMapper[T](),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMapper 获取 Mapper 实例
|
|
||||||
func (s *BaseService[T]) GetMapper() *BaseMapper[T] {
|
|
||||||
return s.mapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// List 分页查询
|
|
||||||
func (s *BaseService[T]) List(page, size int) ([]T, int64, error) {
|
|
||||||
return s.mapper.FindAll(page, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByID 根据 ID 获取
|
|
||||||
func (s *BaseService[T]) GetByID(id string) (*T, error) {
|
|
||||||
return s.mapper.FindByID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create 创建记录(自动生成 ID)
|
|
||||||
func (s *BaseService[T]) Create(item *T) error {
|
|
||||||
// 通过反射设置 ID 字段
|
|
||||||
if err := setID(item); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.mapper.Create(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update 更新记录
|
|
||||||
func (s *BaseService[T]) Update(item *T) error {
|
|
||||||
return s.mapper.Update(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateFields 更新指定字段
|
|
||||||
func (s *BaseService[T]) UpdateFields(id string, fields map[string]interface{}) error {
|
|
||||||
return s.mapper.UpdateFields(id, fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete 删除记录
|
|
||||||
func (s *BaseService[T]) Delete(id string) error {
|
|
||||||
return s.mapper.Delete(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchCreate 批量创建(自动生成 ID)
|
|
||||||
func (s *BaseService[T]) BatchCreate(items []T) error {
|
|
||||||
for i := range items {
|
|
||||||
if err := setID(&items[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.mapper.BatchCreate(items, DefaultBatchSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpdate 批量更新
|
|
||||||
func (s *BaseService[T]) BatchUpdate(items []T) error {
|
|
||||||
return s.mapper.BatchUpdate(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新
|
|
||||||
func (s *BaseService[T]) BatchUpsert(items []T, updateColumns []string) error {
|
|
||||||
for i := range items {
|
|
||||||
if err := setID(&items[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchDelete 批量删除
|
|
||||||
func (s *BaseService[T]) BatchDelete(ids []string) error {
|
|
||||||
return s.mapper.BatchDelete(ids)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setID 通过反射设置 ID 字段
|
|
||||||
func setID(item interface{}) error {
|
|
||||||
val := reflect.ValueOf(item).Elem()
|
|
||||||
|
|
||||||
// 如果当前类型是指针,再次解引用以获取实际的 Struct
|
|
||||||
if val.Kind() == reflect.Ptr {
|
|
||||||
val = val.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if val.Kind() == reflect.Struct {
|
|
||||||
idField := val.FieldByName("ID")
|
|
||||||
if idField.IsValid() && idField.Kind() == reflect.String {
|
|
||||||
// 如果 ID 为空,生成新 ID
|
|
||||||
if idField.String() == "" {
|
|
||||||
idField.SetString(GenerateStringID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -4,81 +4,140 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
"server/common/snowflake"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
分布式/多实例运行(重要)
|
||||||
|
如果你有多个 Pod 或服务器,必须在程序启动时给它们分配不同的 ID,否则还是会冲突
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 获取当前机器的编号,比如从配置文件或环境变量读取
|
||||||
|
// 假设这是第 2 号机器
|
||||||
|
myMachineID := int64(2)
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
err := common.InitGenerator(myMachineID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 之后随处调用
|
||||||
|
println(common.GenerateStringID())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 定义常量 (标准雪花算法配置)
|
||||||
|
const (
|
||||||
|
epoch = int64(1704067200000) // 起始时间戳 2024-01-01
|
||||||
|
workerBits = uint(10) // 机器ID位数
|
||||||
|
sequenceBits = uint(12) // 序列号位数
|
||||||
|
maxWorker = -1 ^ (-1 << workerBits)
|
||||||
|
maxSequence = -1 ^ (-1 << sequenceBits)
|
||||||
|
workerShift = sequenceBits
|
||||||
|
timestampShift = sequenceBits + workerBits
|
||||||
|
)
|
||||||
|
|
||||||
|
type IDGenerator struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
lastTime int64
|
||||||
|
workerID int64
|
||||||
|
sequence int64
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultSnowflake *snowflake.Snowflake
|
defaultGenerator *IDGenerator
|
||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitGenerator 初始化雪花算法生成器
|
// InitGenerator 初始化单例生成器
|
||||||
// workerId: 工作机器ID (0 ~ 31)
|
// 修复:校验逻辑移到 once.Do 外部,防止校验失败消耗掉 once 的执行机会
|
||||||
// datacenterId: 数据中心ID (0 ~ 31)
|
func InitGenerator(workerID int64) error {
|
||||||
// 如果不需要区分数据中心,可以将 datacenterId 设置为 0
|
// 1. 先校验,如果失败直接返回,不要触碰 once
|
||||||
func InitGenerator(workerId, datacenterId int64) error {
|
if workerID < 0 || workerID > int64(maxWorker) {
|
||||||
// 先校验参数
|
return errors.New("worker ID excess of limit (0-1023)")
|
||||||
if workerId < 0 || workerId > 31 {
|
|
||||||
return errors.New("workerId must be between 0 and 31")
|
|
||||||
}
|
|
||||||
if datacenterId < 0 || datacenterId > 31 {
|
|
||||||
return errors.New("datacenterId must be between 0 and 31")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行初始化
|
// 2. 执行初始化
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
var err error
|
defaultGenerator = &IDGenerator{
|
||||||
defaultSnowflake, err = snowflake.NewSnowflake(workerId, datacenterId)
|
workerID: workerID,
|
||||||
if err != nil {
|
lastTime: 0,
|
||||||
panic("InitGenerator failed: " + err.Error())
|
sequence: 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitGeneratorWithWorkerID 仅使用 workerId 初始化(兼容旧版本)
|
// getInstance 获取单例
|
||||||
// datacenterId 默认为 0
|
func getInstance() *IDGenerator {
|
||||||
func InitGeneratorWithWorkerID(workerID int64) error {
|
// 双重检查,虽然 once.Do 是线程安全的,但如果 InitGenerator 没被调用过,
|
||||||
return InitGenerator(workerID, 0)
|
// 我们需要确保这里能兜底初始化
|
||||||
}
|
if defaultGenerator == nil {
|
||||||
|
|
||||||
// getInstance 获取单例实例
|
|
||||||
func getInstance() *snowflake.Snowflake {
|
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
// 默认值:workerId=1, datacenterId=0
|
defaultGenerator = &IDGenerator{
|
||||||
var err error
|
workerID: 1, // 默认机器ID,防止未初始化导致 panic
|
||||||
defaultSnowflake, err = snowflake.NewSnowflake(1, 0)
|
lastTime: 0,
|
||||||
if err != nil {
|
sequence: 0,
|
||||||
// 默认参数如果还失败,直接 panic
|
|
||||||
panic("Snowflake getInstance failed: " + err.Error())
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 防御性编程:如果 once.Do 已经执行过(例如被 InitGenerator 执行了),
|
|
||||||
// 但因为 panic 或其他异常导致 defaultSnowflake 仍为 nil,这里进行补救
|
|
||||||
if defaultSnowflake == nil {
|
|
||||||
// 此时忽略 sync.Once,直接强制初始化,防止 nil pointer crash
|
|
||||||
// 使用默认安全值 (1, 0)
|
|
||||||
defaultSnowflake, _ = snowflake.NewSnowflake(1, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultSnowflake
|
// 【关键修复】如果经过上面的逻辑 defaultGenerator 还是 nil
|
||||||
|
// (这种情况极少见,除非 InitGenerator 曾经被错误调用且没有赋值)
|
||||||
|
// 强制创建一个临时的或抛出 panic,避免空指针崩溃
|
||||||
|
if defaultGenerator == nil {
|
||||||
|
// 最后的兜底,防止崩溃
|
||||||
|
return &IDGenerator{workerID: 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateLongID 生成 64 位整型 ID
|
return defaultGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateLongID 全局辅助函数
|
||||||
func GenerateLongID() int64 {
|
func GenerateLongID() int64 {
|
||||||
id, err := getInstance().NextId()
|
return getInstance().NextID()
|
||||||
if err != nil {
|
|
||||||
// 极端情况:时间回拨
|
|
||||||
// 返回 0 或使用时间戳作为备用方案
|
|
||||||
panic("GenerateLongID failed: " + err.Error())
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateStringID 生成字符串 ID
|
// GenerateStringID 全局辅助函数
|
||||||
func GenerateStringID() string {
|
func GenerateStringID() string {
|
||||||
return strconv.FormatInt(GenerateLongID(), 10)
|
return strconv.FormatInt(getInstance().NextID(), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextID 生成下一个 ID
|
||||||
|
func (g *IDGenerator) NextID() int64 {
|
||||||
|
// 防御性编程:防止 g 为 nil
|
||||||
|
if g == nil {
|
||||||
|
// 如果实例是 nil,尝试获取默认实例
|
||||||
|
if defaultGenerator != nil {
|
||||||
|
g = defaultGenerator
|
||||||
|
} else {
|
||||||
|
// 极端情况,创建一个临时对象(虽然锁不住全局,但能防崩)
|
||||||
|
g = &IDGenerator{workerID: 1}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.mu.Lock()
|
||||||
|
defer g.mu.Unlock()
|
||||||
|
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
|
if now < g.lastTime {
|
||||||
|
now = g.lastTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if now == g.lastTime {
|
||||||
|
g.sequence = (g.sequence + 1) & int64(maxSequence)
|
||||||
|
if g.sequence == 0 {
|
||||||
|
for now <= g.lastTime {
|
||||||
|
now = time.Now().UnixMilli()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.sequence = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
g.lastTime = now
|
||||||
|
|
||||||
|
return ((now - epoch) << timestampShift) | (g.workerID << workerShift) | g.sequence
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
package snowflake
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 定义常量
|
|
||||||
const (
|
|
||||||
// 位数分配
|
|
||||||
sequenceBits = 12 // 序列号占用的位数
|
|
||||||
workerIdBits = 5 // 工作机器ID占用的位数
|
|
||||||
datacenterIdBits = 5 // 数据中心ID占用的位数
|
|
||||||
|
|
||||||
// 最大值
|
|
||||||
maxSequence = -1 ^ (-1 << sequenceBits) // 4095
|
|
||||||
maxWorkerId = -1 ^ (-1 << workerIdBits) // 31
|
|
||||||
maxDatacenterId = -1 ^ (-1 << datacenterIdBits) // 31
|
|
||||||
|
|
||||||
// 位移偏移量
|
|
||||||
workerIdShift = sequenceBits // 12
|
|
||||||
datacenterIdShift = sequenceBits + workerIdBits // 12 + 5 = 17
|
|
||||||
timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits // 12 + 5 + 5 = 22
|
|
||||||
)
|
|
||||||
|
|
||||||
// 起始时间戳 (纪元),可以使用程序上线的时间,这里设置为 2020-01-01 00:00:00 UTC
|
|
||||||
var epoch int64 = 1577836800000
|
|
||||||
|
|
||||||
// Snowflake 结构体
|
|
||||||
type Snowflake struct {
|
|
||||||
mu sync.Mutex // 互斥锁,保证并发安全
|
|
||||||
lastTime int64 // 上次生成ID的时间戳
|
|
||||||
workerId int64 // 工作机器ID
|
|
||||||
datacenterId int64 // 数据中心ID
|
|
||||||
sequence int64 // 当前毫秒内的序列号
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSnowflake 初始化一个 Snowflake 实例
|
|
||||||
// workerId: 工作机器ID (0 ~ 31)
|
|
||||||
// datacenterId: 数据中心ID (0 ~ 31)
|
|
||||||
func NewSnowflake(workerId, datacenterId int64) (*Snowflake, error) {
|
|
||||||
if workerId < 0 || workerId > maxWorkerId {
|
|
||||||
return nil, errors.New(fmt.Sprintf("worker Id can't be greater than %d or less than 0", maxWorkerId))
|
|
||||||
}
|
|
||||||
if datacenterId < 0 || datacenterId > maxDatacenterId {
|
|
||||||
return nil, errors.New(fmt.Sprintf("datacenter Id can't be greater than %d or less than 0", maxDatacenterId))
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Snowflake{
|
|
||||||
lastTime: 0,
|
|
||||||
workerId: workerId,
|
|
||||||
datacenterId: datacenterId,
|
|
||||||
sequence: 0,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NextId 生成下一个 ID
|
|
||||||
func (s *Snowflake) NextId() (int64, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
|
||||||
// 获取当前时间戳(毫秒)
|
|
||||||
now := time.Now().UnixMilli()
|
|
||||||
|
|
||||||
// 如果当前时间小于上次生成ID的时间,说明时钟回拨,抛出异常
|
|
||||||
if now < s.lastTime {
|
|
||||||
return 0, errors.New(fmt.Sprintf("Clock moved backwards. Refusing to generate id for %d milliseconds", s.lastTime-now))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是同一毫秒内
|
|
||||||
if now == s.lastTime {
|
|
||||||
// 序列号自增
|
|
||||||
s.sequence = (s.sequence + 1) & maxSequence
|
|
||||||
// 如果序列号溢出(超过4095),则等待下一毫秒
|
|
||||||
if s.sequence == 0 {
|
|
||||||
now = s.waitNextMillis(now)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 不同毫秒,序列号重置为0
|
|
||||||
s.sequence = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新最后时间戳
|
|
||||||
s.lastTime = now
|
|
||||||
|
|
||||||
// 组装 ID
|
|
||||||
// (当前时间 - 起始时间) << 时间戳位移 | 数据中心ID << 数据中心位移 | 工作ID << 工作位移 | 序列号
|
|
||||||
id := ((now - epoch) << timestampLeftShift) |
|
|
||||||
(s.datacenterId << datacenterIdShift) |
|
|
||||||
(s.workerId << workerIdShift) |
|
|
||||||
s.sequence
|
|
||||||
|
|
||||||
return id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitNextMillis 阻塞等待下一毫秒
|
|
||||||
func (s *Snowflake) waitNextMillis(lastTime int64) int64 {
|
|
||||||
now := time.Now().UnixMilli()
|
|
||||||
for now <= lastTime {
|
|
||||||
now = time.Now().UnixMilli()
|
|
||||||
}
|
|
||||||
return now
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseId 解析 ID,用于调试或查看 ID 组成部分
|
|
||||||
func ParseId(id int64) map[string]interface{} {
|
|
||||||
timestamp := (id >> timestampLeftShift) + epoch
|
|
||||||
datacenterId := (id >> datacenterIdShift) & maxDatacenterId
|
|
||||||
workerId := (id >> workerIdShift) & maxWorkerId
|
|
||||||
sequence := id & maxSequence
|
|
||||||
|
|
||||||
return map[string]interface{}{
|
|
||||||
"id": id,
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"time_str": time.UnixMilli(timestamp).Format("2006-01-02 15:04:05.000"),
|
|
||||||
"datacenterId": datacenterId,
|
|
||||||
"workerId": workerId,
|
|
||||||
"sequence": sequence,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package snowflake // 注意:这里必须是 package snowflake,不能是 main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 这是一个测试函数,用于验证功能
|
|
||||||
func TestGenerateID(t *testing.T) {
|
|
||||||
// 1. 初始化生成器
|
|
||||||
sf, err := NewSnowflake(1, 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("初始化失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("=== 开始生成 ID ===")
|
|
||||||
|
|
||||||
// 2. 生成几个 ID
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
id, err := sf.NextId()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("生成 ID 失败: %v", err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("生成 ID: %d\n", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 解析 ID 查看详情
|
|
||||||
id, _ := sf.NextId()
|
|
||||||
info := ParseId(id)
|
|
||||||
fmt.Printf("\nID 详情解析:\n")
|
|
||||||
fmt.Printf("ID: %d\n", info["id"])
|
|
||||||
fmt.Printf("时间: %s\n", info["time_str"])
|
|
||||||
fmt.Printf("数据中心: %d\n", info["datacenterId"])
|
|
||||||
fmt.Printf("工作机器: %d\n", info["workerId"])
|
|
||||||
fmt.Printf("序列号: %d\n", info["sequence"])
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
server:
|
server:
|
||||||
port: 8081
|
port: 8081
|
||||||
worker_id: 1 # 工作机器ID (0-31),单实例使用1
|
|
||||||
datacenter_id: 0 # 数据中心ID (0-31),默认0 # 雪花算法机器ID (0-1023),分布式环境下不同实例需设置不同值
|
|
||||||
|
|
||||||
log:
|
log:
|
||||||
level: debug
|
level: debug
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ type LogConfig struct {
|
||||||
// ServerConfig 服务配置
|
// ServerConfig 服务配置
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
Port int `yaml:"port"` // 服务端口
|
Port int `yaml:"port"` // 服务端口
|
||||||
WorkerID int `yaml:"worker_id"` // 工作机器ID (0-31),用于雪花算法
|
|
||||||
DatacenterID int `yaml:"datacenter_id"` // 数据中心ID (0-31),用于雪花算法
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecurityConfig 安全配置
|
// SecurityConfig 安全配置
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
server:
|
server:
|
||||||
port: 8081
|
port: 8081
|
||||||
worker_id: 1 # 工作机器ID (0-31),多实例部署需配置不同值
|
|
||||||
datacenter_id: 0 # 数据中心ID (0-31),多机房部署需配置不同值 # 雪花算法机器ID (0-1023),分布式环境下不同实例需设置不同值,多实例部署时需手动配置
|
|
||||||
|
|
||||||
log:
|
log:
|
||||||
level: info
|
level: info
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8080
|
||||||
worker_id: 1 # 工作机器ID (0-31),测试环境使用1
|
|
||||||
datacenter_id: 0 # 数据中心ID (0-31),默认0
|
|
||||||
|
|
||||||
log:
|
log:
|
||||||
level: debug
|
level: debug
|
||||||
|
|
|
||||||
|
|
@ -41,21 +41,6 @@ func main() {
|
||||||
common.InitLogger()
|
common.InitLogger()
|
||||||
common.Info("========== 应用启动 ==========")
|
common.Info("========== 应用启动 ==========")
|
||||||
|
|
||||||
// 初始化雪花算法ID生成器(从配置获取workerID,默认为1)
|
|
||||||
workerID := int64(config.AppConfig.Server.WorkerID)
|
|
||||||
if workerID <= 0 {
|
|
||||||
workerID = 1 // 默认workerID
|
|
||||||
}
|
|
||||||
datacenterID := int64(config.AppConfig.Server.DatacenterID)
|
|
||||||
if datacenterID < 0 {
|
|
||||||
datacenterID = 0 // 默认datacenterID
|
|
||||||
}
|
|
||||||
if err := common.InitGenerator(workerID, datacenterID); err != nil {
|
|
||||||
common.LogError("雪花算法初始化失败: %v", err)
|
|
||||||
log.Fatalf("雪花算法初始化失败: %v\n", err)
|
|
||||||
}
|
|
||||||
common.Info("雪花算法ID生成器初始化完成 (WorkerID: %d)", workerID)
|
|
||||||
|
|
||||||
// 初始化数据库
|
// 初始化数据库
|
||||||
config.InitDB()
|
config.InitDB()
|
||||||
common.Info("数据库初始化完成")
|
common.Info("数据库初始化完成")
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,55 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/system/entity"
|
"server/modules/system/entity"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysUserMapper struct {
|
type SysUserMapper struct{}
|
||||||
*common.BaseMapper[entity.SysUser]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSysUserMapper() *SysUserMapper {
|
func NewSysUserMapper() *SysUserMapper {
|
||||||
return &SysUserMapper{
|
return &SysUserMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.SysUser](),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDB 获取数据库实例,添加逻辑删除过滤
|
func (m *SysUserMapper) FindAll(page, size int) ([]entity.SysUser, int64, error) {
|
||||||
func (m *SysUserMapper) GetDB() *gorm.DB {
|
var items []entity.SysUser
|
||||||
return m.BaseMapper.GetDB().Where("del_flag = 0")
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 逻辑删除
|
func (m *SysUserMapper) FindByID(id string) (*entity.SysUser, error) {
|
||||||
func (m *SysUserMapper) Delete(id string) error {
|
var item entity.SysUser
|
||||||
return m.BaseMapper.GetDB().Model(&entity.SysUser{}).Where("id = ?", id).Update("del_flag", 1).Error
|
err := config.DB.First(&item, "id = ? AND del_flag = 0", id).Error
|
||||||
|
return &item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByUsername 根据用户名查找用户
|
|
||||||
func (m *SysUserMapper) FindByUsername(username string) (*entity.SysUser, error) {
|
func (m *SysUserMapper) FindByUsername(username string) (*entity.SysUser, error) {
|
||||||
var item entity.SysUser
|
var item entity.SysUser
|
||||||
err := m.GetDB().First(&item, "username = ?", username).Error
|
err := config.DB.First(&item, "username = ? AND del_flag = 0", username).Error
|
||||||
return &item, err
|
return &item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByPhone 根据手机号查找用户
|
|
||||||
func (m *SysUserMapper) FindByPhone(phone string) (*entity.SysUser, error) {
|
func (m *SysUserMapper) FindByPhone(phone string) (*entity.SysUser, error) {
|
||||||
var item entity.SysUser
|
var item entity.SysUser
|
||||||
err := m.GetDB().First(&item, "phone = ?", phone).Error
|
err := config.DB.First(&item, "phone = ? AND del_flag = 0", phone).Error
|
||||||
return &item, err
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,36 +18,37 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type SysUserService struct {
|
type SysUserService struct {
|
||||||
*common.BaseService[entity.SysUser]
|
|
||||||
mapper *mapper.SysUserMapper
|
mapper *mapper.SysUserMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSysUserService() *SysUserService {
|
func NewSysUserService() *SysUserService {
|
||||||
mapper := mapper.NewSysUserMapper()
|
return &SysUserService{mapper: mapper.NewSysUserMapper()}
|
||||||
return &SysUserService{
|
|
||||||
BaseService: common.NewBaseService[entity.SysUser](),
|
|
||||||
mapper: mapper,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 用户登录(手机号登录)
|
// Login 用户登录
|
||||||
|
// 密码验证方式与 Java JeecgBoot 兼容: PBEWithMD5AndDES(username, password, salt)
|
||||||
func (s *SysUserService) Login(username, password string) (*entity.LoginUser, string, error) {
|
func (s *SysUserService) Login(username, password string) (*entity.LoginUser, string, error) {
|
||||||
|
// 查询用户
|
||||||
user, err := s.mapper.FindByPhone(username)
|
user, err := s.mapper.FindByPhone(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New("用户不存在")
|
return nil, "", errors.New("用户不存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证状态
|
||||||
if user.Status == 2 {
|
if user.Status == 2 {
|
||||||
return nil, "", errors.New("账号已被冻结")
|
return nil, "", errors.New("账号已被冻结")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证密码 (与Java兼容: encrypt(username, password, salt))
|
||||||
encrypted, err := common.Encrypt(user.Username, password, user.Salt)
|
encrypted, err := common.Encrypt(user.Username, password, user.Salt)
|
||||||
if (user.Password != encrypted) || (err != nil) {
|
if (user.Password != encrypted) || (err != nil) {
|
||||||
return nil, "", errors.New("用户名或密码错误")
|
return nil, "", errors.New("用户名或密码错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成Token
|
||||||
token := s.generateToken()
|
token := s.generateToken()
|
||||||
|
|
||||||
|
// 构建登录用户信息
|
||||||
loginUser := &entity.LoginUser{
|
loginUser := &entity.LoginUser{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
|
|
@ -58,6 +59,7 @@ func (s *SysUserService) Login(username, password string) (*entity.LoginUser, st
|
||||||
Token: token,
|
Token: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 存储到Redis
|
||||||
if err := s.saveLoginUser(token, loginUser); err != nil {
|
if err := s.saveLoginUser(token, loginUser); err != nil {
|
||||||
return nil, "", errors.New("登录失败,请重试")
|
return nil, "", errors.New("登录失败,请重试")
|
||||||
}
|
}
|
||||||
|
|
@ -65,24 +67,30 @@ func (s *SysUserService) Login(username, password string) (*entity.LoginUser, st
|
||||||
return loginUser, token, nil
|
return loginUser, token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SysLogin 用户登录(用户名登录)
|
// SysLogin 用户登录
|
||||||
|
// 密码验证方式与 Java JeecgBoot 兼容: PBEWithMD5AndDES(username, password, salt)
|
||||||
func (s *SysUserService) SysLogin(username, password string) (*entity.LoginUser, string, error) {
|
func (s *SysUserService) SysLogin(username, password string) (*entity.LoginUser, string, error) {
|
||||||
|
// 查询用户
|
||||||
user, err := s.mapper.FindByUsername(username)
|
user, err := s.mapper.FindByUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New("用户不存在")
|
return nil, "", errors.New("用户不存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证状态
|
||||||
if user.Status == 2 {
|
if user.Status == 2 {
|
||||||
return nil, "", errors.New("账号已被冻结")
|
return nil, "", errors.New("账号已被冻结")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证密码 (与Java兼容: encrypt(username, password, salt))
|
||||||
encrypted, err := common.Encrypt(username, password, user.Salt)
|
encrypted, err := common.Encrypt(username, password, user.Salt)
|
||||||
if (user.Password != encrypted) || (err != nil) {
|
if (user.Password != encrypted) || (err != nil) {
|
||||||
return nil, "", errors.New("用户名或密码错误")
|
return nil, "", errors.New("用户名或密码错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成Token
|
||||||
token := s.generateToken()
|
token := s.generateToken()
|
||||||
|
|
||||||
|
// 构建登录用户信息
|
||||||
loginUser := &entity.LoginUser{
|
loginUser := &entity.LoginUser{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
|
|
@ -93,6 +101,7 @@ func (s *SysUserService) SysLogin(username, password string) (*entity.LoginUser,
|
||||||
Token: token,
|
Token: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 存储到Redis
|
||||||
if err := s.saveLoginUser(token, loginUser); err != nil {
|
if err := s.saveLoginUser(token, loginUser); err != nil {
|
||||||
return nil, "", errors.New("登录失败,请重试")
|
return nil, "", errors.New("登录失败,请重试")
|
||||||
}
|
}
|
||||||
|
|
@ -119,6 +128,7 @@ func (s *SysUserService) GetLoginUser(token string) (*entity.LoginUser, error) {
|
||||||
return nil, errors.New("登录信息异常")
|
return nil, errors.New("登录信息异常")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新过期时间
|
||||||
config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire)
|
config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire)
|
||||||
|
|
||||||
return &loginUser, nil
|
return &loginUser, nil
|
||||||
|
|
@ -130,33 +140,7 @@ func (s *SysUserService) RefreshToken(token string) error {
|
||||||
return config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire).Err()
|
return config.RDB.Expire(ctx, common.RedisTokenPrefix+token, common.RedisTokenExpire).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePassword 修改密码
|
// 保存登录用户到Redis
|
||||||
func (s *SysUserService) UpdatePassword(id, oldPwd, newPwd string) error {
|
|
||||||
user, err := s.GetByID(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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// saveLoginUser 保存登录用户到Redis
|
|
||||||
func (s *SysUserService) saveLoginUser(token string, user *entity.LoginUser) error {
|
func (s *SysUserService) saveLoginUser(token string, user *entity.LoginUser) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
data, err := json.Marshal(user)
|
data, err := json.Marshal(user)
|
||||||
|
|
@ -166,19 +150,30 @@ func (s *SysUserService) saveLoginUser(token string, user *entity.LoginUser) err
|
||||||
return config.RDB.Set(ctx, common.RedisTokenPrefix+token, data, common.RedisTokenExpire).Err()
|
return config.RDB.Set(ctx, common.RedisTokenPrefix+token, data, common.RedisTokenExpire).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateToken 生成Token
|
// 生成Token
|
||||||
func (s *SysUserService) generateToken() string {
|
func (s *SysUserService) generateToken() string {
|
||||||
return uuid.New().String()
|
return uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建用户(添加密码加密逻辑)
|
// ========== 用户管理 ==========
|
||||||
|
|
||||||
|
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 {
|
func (s *SysUserService) Create(item *entity.SysUser) error {
|
||||||
item.ID = uuid.New().String()
|
item.ID = uuid.New().String()
|
||||||
|
// 生成盐值 (8字节)
|
||||||
item.Salt = uuid.New().String()[:8]
|
item.Salt = uuid.New().String()[:8]
|
||||||
|
// 加密密码 (与Java兼容)
|
||||||
encrypted, err := common.Encrypt(item.Username, item.Password, item.Salt)
|
encrypted, err := common.Encrypt(item.Username, item.Password, item.Salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("密码加密失败: %v", err)
|
log.Printf("密码加密失败: %v", err)
|
||||||
return fmt.Errorf("密码加密失败: %w,请联系管理员", err)
|
return fmt.Errorf("密码加密失败: %w,请联系管理员", err) // 仍然返回错误
|
||||||
}
|
}
|
||||||
item.Password = encrypted
|
item.Password = encrypted
|
||||||
item.DelFlag = 0
|
item.DelFlag = 0
|
||||||
|
|
@ -187,3 +182,45 @@ func (s *SysUserService) Create(item *entity.SysUser) error {
|
||||||
item.CreateTime = &now
|
item.CreateTime = &now
|
||||||
return s.mapper.Create(item)
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxCalculationMajorMapper struct {
|
type YxCalculationMajorMapper struct{}
|
||||||
*common.BaseMapper[entity.YxCalculationMajor]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxCalculationMajorMapper() *YxCalculationMajorMapper {
|
func NewYxCalculationMajorMapper() *YxCalculationMajorMapper {
|
||||||
return &YxCalculationMajorMapper{
|
return &YxCalculationMajorMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxCalculationMajor](),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryCostTime 存储各协程耗时的结构体
|
// 先定义存储各协程耗时的结构体(局部使用,也可全局复用)
|
||||||
type QueryCostTime struct {
|
type QueryCostTime struct {
|
||||||
CountCost time.Duration // 总数量查询耗时
|
CountCost time.Duration // 总数量查询耗时
|
||||||
ProbCountCost time.Duration // 四种概率数量查询耗时
|
ProbCountCost time.Duration // 四种概率数量查询耗时
|
||||||
|
|
@ -30,7 +28,15 @@ type QueryCostTime struct {
|
||||||
TotalCost time.Duration // 整体总耗时
|
TotalCost time.Duration // 整体总耗时
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindRecommendList 查询推荐专业列表(优化版:并发查询总数量、概率数量、主列表)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调整返回值:新增 ProbabilityCountDTO,返回列表、总数量、四种概率各自数量
|
||||||
func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery) ([]dto.UserMajorDTO, int64, dto.ProbabilityCountDTO, error) {
|
func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery) ([]dto.UserMajorDTO, int64, dto.ProbabilityCountDTO, error) {
|
||||||
var items []dto.UserMajorDTO
|
var items []dto.UserMajorDTO
|
||||||
var total int64
|
var total int64
|
||||||
|
|
@ -41,6 +47,9 @@ func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery)
|
||||||
if tableName == "" {
|
if tableName == "" {
|
||||||
return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("CalculationTableName is empty")
|
return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("CalculationTableName is empty")
|
||||||
}
|
}
|
||||||
|
// if !validTableNames[] {
|
||||||
|
// return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("invalid table name: %s, potential SQL injection risk", tableName)
|
||||||
|
// }
|
||||||
|
|
||||||
// 2. 基础条件SQL(共用过滤条件,排除概率筛选)
|
// 2. 基础条件SQL(共用过滤条件,排除概率筛选)
|
||||||
baseSQL := " WHERE 1=1 AND cm.state > 0 "
|
baseSQL := " WHERE 1=1 AND cm.state > 0 "
|
||||||
|
|
@ -166,6 +175,7 @@ func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery)
|
||||||
mainSQL += fmt.Sprintf(" LIMIT %d OFFSET %d", size, offset)
|
mainSQL += fmt.Sprintf(" LIMIT %d OFFSET %d", size, offset)
|
||||||
|
|
||||||
// 7. 协程并发执行三个查询(总数量、概率数量、主列表),提升性能
|
// 7. 协程并发执行三个查询(总数量、概率数量、主列表),提升性能
|
||||||
|
// ---------------------- 核心局部代码(替换你原来的协程块) ----------------------
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var countErr, probCountErr, queryErr error
|
var countErr, probCountErr, queryErr error
|
||||||
var queryCost QueryCostTime // 存储各协程耗时
|
var queryCost QueryCostTime // 存储各协程耗时
|
||||||
|
|
@ -240,7 +250,6 @@ func (m *YxCalculationMajorMapper) FindRecommendList(query dto.SchoolMajorQuery)
|
||||||
return items, total, probCount, nil
|
return items, total, probCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindRecommendList1 查询推荐专业列表(原版)
|
|
||||||
func (m *YxCalculationMajorMapper) FindRecommendList1(query dto.SchoolMajorQuery) ([]dto.UserMajorDTO, int64, error) {
|
func (m *YxCalculationMajorMapper) FindRecommendList1(query dto.SchoolMajorQuery) ([]dto.UserMajorDTO, int64, error) {
|
||||||
var items []dto.UserMajorDTO
|
var items []dto.UserMajorDTO
|
||||||
var total int64
|
var total int64
|
||||||
|
|
@ -348,6 +357,8 @@ func (m *YxCalculationMajorMapper) FindRecommendList1(query dto.SchoolMajorQuery
|
||||||
sql += " AND (cm.enroll_probability >= 93)"
|
sql += " AND (cm.enroll_probability >= 93)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 移除了无效的 strings.Replace
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var countErr, queryErr error
|
var countErr, queryErr error
|
||||||
|
|
||||||
|
|
@ -371,14 +382,34 @@ func (m *YxCalculationMajorMapper) FindRecommendList1(query dto.SchoolMajorQuery
|
||||||
return items, total, queryErr
|
return items, total, queryErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByScoreID 根据 scoreID 查找计算专业列表
|
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) {
|
func (m *YxCalculationMajorMapper) FindByScoreID(scoreID string) ([]entity.YxCalculationMajor, error) {
|
||||||
var items []entity.YxCalculationMajor
|
var items []entity.YxCalculationMajor
|
||||||
err := m.GetDB().Where("score_id = ?", scoreID).Find(&items).Error
|
err := config.DB.Where("score_id = ?", scoreID).Find(&items).Error
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindListByCompositeKeys 根据复合键查找计算专业列表
|
|
||||||
func (m *YxCalculationMajorMapper) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
|
func (m *YxCalculationMajorMapper) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -395,7 +426,7 @@ func (m *YxCalculationMajorMapper) FindListByCompositeKeys(tableName string, key
|
||||||
}
|
}
|
||||||
|
|
||||||
var items []entity.YxCalculationMajor
|
var items []entity.YxCalculationMajor
|
||||||
db := m.GetDB()
|
db := config.DB
|
||||||
if tableName != "" {
|
if tableName != "" {
|
||||||
db = db.Table(tableName)
|
db = db.Table(tableName)
|
||||||
}
|
}
|
||||||
|
|
@ -423,7 +454,6 @@ func (m *YxCalculationMajorMapper) FindListByCompositeKeys(tableName string, key
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindDtoListByCompositeKeys 根据复合键查找 DTO 列表
|
|
||||||
func (m *YxCalculationMajorMapper) FindDtoListByCompositeKeys(tableName string, keys []string, scoreId string) ([]dto.SchoolMajorDTO, error) {
|
func (m *YxCalculationMajorMapper) FindDtoListByCompositeKeys(tableName string, keys []string, scoreId string) ([]dto.SchoolMajorDTO, error) {
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -496,27 +526,39 @@ func (m *YxCalculationMajorMapper) FindDtoListByCompositeKeys(tableName string,
|
||||||
|
|
||||||
sqlStr += strings.Join(tuples, ",") + ")"
|
sqlStr += strings.Join(tuples, ",") + ")"
|
||||||
|
|
||||||
err := m.GetDB().Raw(sqlStr, params...).Scan(&items).Error
|
err := config.DB.Raw(sqlStr, params...).Scan(&items).Error
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchCreate 批量创建(支持动态表名)
|
|
||||||
func (m *YxCalculationMajorMapper) BatchCreate(tableName string, items []entity.YxCalculationMajor, batchSize int) error {
|
func (m *YxCalculationMajorMapper) BatchCreate(tableName string, items []entity.YxCalculationMajor, batchSize int) error {
|
||||||
if tableName != "" {
|
if tableName != "" {
|
||||||
return m.GetDB().Table(tableName).CreateInBatches(items, batchSize).Error
|
return config.DB.Table(tableName).CreateInBatches(items, batchSize).Error
|
||||||
}
|
}
|
||||||
return m.GetDB().CreateInBatches(items, batchSize).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
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByScoreID 根据 scoreID 删除
|
|
||||||
func (m *YxCalculationMajorMapper) DeleteByScoreID(scoreID string) error {
|
func (m *YxCalculationMajorMapper) DeleteByScoreID(scoreID string) error {
|
||||||
return m.GetDB().Delete(&entity.YxCalculationMajor{}, "score_id = ?", scoreID).Error
|
return config.DB.Delete(&entity.YxCalculationMajor{}, "score_id = ?", scoreID).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByScoreIDFromTable 从指定表删除 scoreID 对应的数据
|
|
||||||
func (m *YxCalculationMajorMapper) DeleteByScoreIDFromTable(tableName, scoreID string) error {
|
func (m *YxCalculationMajorMapper) DeleteByScoreIDFromTable(tableName, scoreID string) error {
|
||||||
if tableName == "" {
|
if tableName == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return m.GetDB().Table(tableName).Where("score_id = ?", scoreID).Delete(map[string]interface{}{}).Error
|
return config.DB.Table(tableName).Where("score_id = ?", scoreID).Delete(map[string]interface{}{}).Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,69 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxHistoryMajorEnrollMapper struct {
|
type YxHistoryMajorEnrollMapper struct{}
|
||||||
*common.BaseMapper[entity.YxHistoryMajorEnroll]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxHistoryMajorEnrollMapper() *YxHistoryMajorEnrollMapper {
|
func NewYxHistoryMajorEnrollMapper() *YxHistoryMajorEnrollMapper {
|
||||||
return &YxHistoryMajorEnrollMapper{
|
return &YxHistoryMajorEnrollMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxHistoryMajorEnroll](),
|
}
|
||||||
}
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByYear 根据年份查找历史招生数据
|
|
||||||
func (m *YxHistoryMajorEnrollMapper) FindByYear(year string) ([]entity.YxHistoryMajorEnroll, error) {
|
func (m *YxHistoryMajorEnrollMapper) FindByYear(year string) ([]entity.YxHistoryMajorEnroll, error) {
|
||||||
var items []entity.YxHistoryMajorEnroll
|
var items []entity.YxHistoryMajorEnroll
|
||||||
err := m.GetDB().Where("year = ?", year).Find(&items).Error
|
err := config.DB.Where("year = ?", year).Find(&items).Error
|
||||||
return items, err
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,26 +2,130 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxSchoolMajorMapper struct {
|
type YxSchoolMajorMapper struct{}
|
||||||
*common.BaseMapper[entity.YxSchoolMajor]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxSchoolMajorMapper() *YxSchoolMajorMapper {
|
func NewYxSchoolMajorMapper() *YxSchoolMajorMapper {
|
||||||
return &YxSchoolMajorMapper{
|
return &YxSchoolMajorMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxSchoolMajor](),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectSchoolMajor 查询院校专业信息(包含学校信息)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (m *YxSchoolMajorMapper) SelectSchoolMajor(query dto.SchoolMajorQuery) ([]dto.SchoolMajorDTO, error) {
|
||||||
|
// var items []dto.SchoolMajorDTO
|
||||||
|
|
||||||
|
// sql := `
|
||||||
|
// SELECT
|
||||||
|
// sm.school_code,
|
||||||
|
// sc.school_name,
|
||||||
|
// sm.major_code,
|
||||||
|
// sm.major_name,
|
||||||
|
// sm.major_type,
|
||||||
|
// sm.major_type_child,
|
||||||
|
// sm.plan_num,
|
||||||
|
// sm.main_subjects,
|
||||||
|
// sm.limitation,
|
||||||
|
// sm.chinese_score_limitation,
|
||||||
|
// sm.english_score_limitation,
|
||||||
|
// sm.cultural_score_limitation,
|
||||||
|
// sm.professional_score_limitation,
|
||||||
|
// sm.enrollment_code,
|
||||||
|
// sm.tuition,
|
||||||
|
// sm.detail,
|
||||||
|
// sm.category,
|
||||||
|
// sm.batch,
|
||||||
|
// sm.rules_enroll_probability,
|
||||||
|
// sm.probability_operator,
|
||||||
|
// sm.private_rules_enroll_probability,
|
||||||
|
// sm.private_probability_operator,
|
||||||
|
// sm.rules_enroll_probability_sx,
|
||||||
|
// sm.kslx,
|
||||||
|
// sm.state
|
||||||
|
// FROM yx_school_major sm
|
||||||
|
// LEFT JOIN (SELECT school_id,school_name,school_code FROM yx_school_child group by school_code) sc ON sc.school_code = sm.school_code
|
||||||
|
// LEFT JOIN yx_school s ON s.id = sc.school_id
|
||||||
|
// WHERE 1=1 AND sm.state > 0
|
||||||
|
// `
|
||||||
|
// params := []interface{}{}
|
||||||
|
|
||||||
|
// if query.MajorType != "" {
|
||||||
|
// sql += " AND sm.major_type = ?"
|
||||||
|
// params = append(params, query.MajorType)
|
||||||
|
// }
|
||||||
|
// if query.Category != "" {
|
||||||
|
// sql += " AND sm.category = ?"
|
||||||
|
// params = append(params, query.Category)
|
||||||
|
// }
|
||||||
|
// if len(query.MajorTypeChildren) > 0 {
|
||||||
|
// sql += " AND sm.major_type_child IN ?"
|
||||||
|
// params = append(params, query.MajorTypeChildren)
|
||||||
|
// }
|
||||||
|
// if query.MainSubjects != "" {
|
||||||
|
// sql += " AND sm.main_subjects = ?"
|
||||||
|
// params = append(params, query.MainSubjects)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// err := config.DB.Raw(sql, params...).Scan(&items).Error
|
||||||
|
// return items, err
|
||||||
|
// }
|
||||||
|
|
||||||
func (m *YxSchoolMajorMapper) SelectSchoolMajor(query dto.SchoolMajorQuery) ([]dto.SchoolMajorDTO, error) {
|
func (m *YxSchoolMajorMapper) SelectSchoolMajor(query dto.SchoolMajorQuery) ([]dto.SchoolMajorDTO, error) {
|
||||||
var items []dto.SchoolMajorDTO
|
var items []dto.SchoolMajorDTO
|
||||||
|
|
||||||
queryBuilder := m.GetDB().Table("yx_school_major sm").
|
queryBuilder := config.DB.Table("yx_school_major sm").
|
||||||
Select(`
|
Select(`
|
||||||
sm.school_code,
|
sm.school_code,
|
||||||
sc.school_name,
|
sc.school_name,
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,46 @@ package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxUserScoreMapper struct {
|
type YxUserScoreMapper struct{}
|
||||||
*common.BaseMapper[entity.YxUserScore]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxUserScoreMapper() *YxUserScoreMapper {
|
func NewYxUserScoreMapper() *YxUserScoreMapper {
|
||||||
return &YxUserScoreMapper{
|
return &YxUserScoreMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxUserScore](),
|
}
|
||||||
}
|
|
||||||
|
func (m *YxUserScoreMapper) FindAll(page, size int) ([]entity.YxUserScore, int64, error) {
|
||||||
|
var items []entity.YxUserScore
|
||||||
|
var total int64
|
||||||
|
config.DB.Model(&entity.YxUserScore{}).Count(&total)
|
||||||
|
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
|
||||||
|
return items, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) FindByID(id string) (*entity.YxUserScore, error) {
|
||||||
|
var item entity.YxUserScore
|
||||||
|
err := config.DB.First(&item, "id = ?", id).Error
|
||||||
|
return &item, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) Create(item *entity.YxUserScore) error {
|
||||||
|
return config.DB.Create(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) Update(item *entity.YxUserScore) error {
|
||||||
|
return config.DB.Save(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return config.DB.Model(&entity.YxUserScore{}).Where("id = ?", id).Updates(fields).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFieldsByMultiCondition 根据多个条件更新字段
|
|
||||||
func (m *YxUserScoreMapper) UpdateFieldsByMultiCondition(condition *entity.YxUserScore, fields map[string]interface{}) error {
|
func (m *YxUserScoreMapper) UpdateFieldsByMultiCondition(condition *entity.YxUserScore, fields map[string]interface{}) error {
|
||||||
query := m.GetDB().Model(&entity.YxUserScore{})
|
query := config.DB.Model(&entity.YxUserScore{})
|
||||||
|
|
||||||
whereCount := 0 // 记录条件数量,避免无条件更新
|
whereCount := 0 // 记录条件数量,避免无条件更新
|
||||||
if condition.ID != "" {
|
if condition.ID != "" {
|
||||||
|
|
@ -42,3 +65,26 @@ func (m *YxUserScoreMapper) UpdateFieldsByMultiCondition(condition *entity.YxUse
|
||||||
|
|
||||||
return query.Updates(fields).Error
|
return query.Updates(fields).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) Delete(id string) error {
|
||||||
|
return config.DB.Delete(&entity.YxUserScore{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) BatchCreate(items []entity.YxUserScore, batchSize int) error {
|
||||||
|
return config.DB.CreateInBatches(items, batchSize).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) BatchUpdate(items []entity.YxUserScore) error {
|
||||||
|
return config.DB.Save(items).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) BatchUpsert(items []entity.YxUserScore, updateColumns []string) error {
|
||||||
|
return config.DB.Clauses(clause.OnConflict{
|
||||||
|
Columns: []clause.Column{{Name: "id"}},
|
||||||
|
DoUpdates: clause.AssignmentColumns(updateColumns),
|
||||||
|
}).CreateInBatches(items, 100).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxUserScoreMapper) BatchDelete(ids []string) error {
|
||||||
|
return config.DB.Delete(&entity.YxUserScore{}, "id IN ?", ids).Error
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,17 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/vo"
|
"server/modules/yx/vo"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxVolunteerMapper struct {
|
type YxVolunteerMapper struct{}
|
||||||
*common.BaseMapper[entity.YxVolunteer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewYxVolunteerMapper() *YxVolunteerMapper {
|
|
||||||
return &YxVolunteerMapper{
|
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxVolunteer](),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseOtherVolunteer 关闭用户的其他志愿单
|
|
||||||
func (m *YxVolunteerMapper) CloseOtherVolunteer(userId string) error {
|
func (m *YxVolunteerMapper) CloseOtherVolunteer(userId string) error {
|
||||||
result := m.GetDB().Model(&entity.YxVolunteer{}).
|
result := config.DB.Model(&entity.YxVolunteer{}).
|
||||||
Where("create_by = ?", userId).
|
Where("create_by = ?", userId).
|
||||||
Updates(map[string]interface{}{"state": "0"})
|
Updates(map[string]interface{}{"state": "0"})
|
||||||
|
|
||||||
|
|
@ -29,19 +22,70 @@ func (m *YxVolunteerMapper) CloseOtherVolunteer(userId string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindActiveByScoreId 根据 scoreId 查找激活的志愿单
|
func NewYxVolunteerMapper() *YxVolunteerMapper {
|
||||||
func (m *YxVolunteerMapper) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
|
return &YxVolunteerMapper{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) FindAll(page, size int) ([]entity.YxVolunteer, int64, error) {
|
||||||
|
var items []entity.YxVolunteer
|
||||||
|
var total int64
|
||||||
|
config.DB.Model(&entity.YxVolunteer{}).Count(&total)
|
||||||
|
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
|
||||||
|
return items, total, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) FindByID(id string) (*entity.YxVolunteer, error) {
|
||||||
var item entity.YxVolunteer
|
var item entity.YxVolunteer
|
||||||
err := m.GetDB().Where("score_id = ? AND state = ?", scoreId, "1").First(&item).Error
|
err := config.DB.First(&item, "id = ?", id).Error
|
||||||
return &item, err
|
return &item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByUser 查询用户的志愿单列表(包含成绩信息)
|
func (m *YxVolunteerMapper) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
|
||||||
|
var item entity.YxVolunteer
|
||||||
|
err := config.DB.Where("score_id = ? AND state = ?", scoreId, "1").First(&item).Error
|
||||||
|
return &item, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) Create(item *entity.YxVolunteer) error {
|
||||||
|
return config.DB.Create(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) Update(item *entity.YxVolunteer) error {
|
||||||
|
return config.DB.Save(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return config.DB.Model(&entity.YxVolunteer{}).Where("id = ?", id).Updates(fields).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) Delete(id string) error {
|
||||||
|
return config.DB.Delete(&entity.YxVolunteer{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) BatchCreate(items []entity.YxVolunteer, batchSize int) error {
|
||||||
|
return config.DB.CreateInBatches(items, batchSize).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) BatchUpdate(items []entity.YxVolunteer) error {
|
||||||
|
return config.DB.Save(items).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) BatchUpsert(items []entity.YxVolunteer, updateColumns []string) error {
|
||||||
|
return config.DB.Clauses(clause.OnConflict{
|
||||||
|
Columns: []clause.Column{{Name: "id"}},
|
||||||
|
DoUpdates: clause.AssignmentColumns(updateColumns),
|
||||||
|
}).CreateInBatches(items, 100).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerMapper) BatchDelete(ids []string) error {
|
||||||
|
return config.DB.Delete(&entity.YxVolunteer{}, "id IN ?", ids).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (m *YxVolunteerMapper) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
func (m *YxVolunteerMapper) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
||||||
var items []vo.UserVolunteerVO
|
var items []vo.UserVolunteerVO
|
||||||
var total int64
|
var total int64
|
||||||
|
|
||||||
db := m.GetDB().Table("yx_volunteer v").
|
db := config.DB.Table("yx_volunteer v").
|
||||||
Select("v.*, s.professional_category, s.province, s.cultural_score, s.professional_score").
|
Select("v.*, s.professional_category, s.province, s.cultural_score, s.professional_score").
|
||||||
Joins("LEFT JOIN yx_user_score s ON v.score_id = s.id").
|
Joins("LEFT JOIN yx_user_score s ON v.score_id = s.id").
|
||||||
Where("v.create_by = ?", userID)
|
Where("v.create_by = ?", userID)
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,77 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
"server/config"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
|
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxVolunteerRecordMapper struct {
|
type YxVolunteerRecordMapper struct{}
|
||||||
*common.BaseMapper[entity.YxVolunteerRecord]
|
|
||||||
|
func (m *YxVolunteerRecordMapper) BatchDeleteByVolunteerId(id string) {
|
||||||
|
config.DB.Delete(&entity.YxVolunteerRecord{}, "volunteer_id = ?", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxVolunteerRecordMapper() *YxVolunteerRecordMapper {
|
func NewYxVolunteerRecordMapper() *YxVolunteerRecordMapper {
|
||||||
return &YxVolunteerRecordMapper{
|
return &YxVolunteerRecordMapper{}
|
||||||
BaseMapper: common.NewBaseMapper[entity.YxVolunteerRecord](),
|
}
|
||||||
}
|
|
||||||
|
func (m *YxVolunteerRecordMapper) FindAll(page, size int) ([]entity.YxVolunteerRecord, int64, error) {
|
||||||
|
var items []entity.YxVolunteerRecord
|
||||||
|
var total int64
|
||||||
|
config.DB.Model(&entity.YxVolunteerRecord{}).Count(&total)
|
||||||
|
err := config.DB.Offset((page - 1) * size).Limit(size).Find(&items).Error
|
||||||
|
return items, total, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByVolunteerID 根据 volunteerID 查找志愿记录
|
|
||||||
func (m *YxVolunteerRecordMapper) FindByVolunteerID(volunteerID string) ([]entity.YxVolunteerRecord, error) {
|
func (m *YxVolunteerRecordMapper) FindByVolunteerID(volunteerID string) ([]entity.YxVolunteerRecord, error) {
|
||||||
var items []entity.YxVolunteerRecord
|
var items []entity.YxVolunteerRecord
|
||||||
err := m.GetDB().Where("volunteer_id = ?", volunteerID).Order("indexs ASC").Find(&items).Error
|
err := config.DB.Where("volunteer_id = ?", volunteerID).Order("indexs ASC").Find(&items).Error
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByVolunteerID 根据 volunteerID 删除志愿记录
|
func (m *YxVolunteerRecordMapper) FindByID(id string) (*entity.YxVolunteerRecord, error) {
|
||||||
func (m *YxVolunteerRecordMapper) DeleteByVolunteerID(volunteerID string) error {
|
var item entity.YxVolunteerRecord
|
||||||
return m.GetDB().Delete(&entity.YxVolunteerRecord{}, "volunteer_id = ?", volunteerID).Error
|
err := config.DB.First(&item, "id = ?", id).Error
|
||||||
|
return &item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchDeleteByVolunteerId 根据 volunteerID 批量删除志愿记录(无返回值)
|
func (m *YxVolunteerRecordMapper) Create(item *entity.YxVolunteerRecord) error {
|
||||||
func (m *YxVolunteerRecordMapper) BatchDeleteByVolunteerId(id string) {
|
return config.DB.Create(item).Error
|
||||||
m.GetDB().Delete(&entity.YxVolunteerRecord{}, "volunteer_id = ?", id)
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) Update(item *entity.YxVolunteerRecord) error {
|
||||||
|
return config.DB.Save(item).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return config.DB.Model(&entity.YxVolunteerRecord{}).Where("id = ?", id).Updates(fields).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) Delete(id string) error {
|
||||||
|
return config.DB.Delete(&entity.YxVolunteerRecord{}, "id = ?", id).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) DeleteByVolunteerID(volunteerID string) error {
|
||||||
|
return config.DB.Delete(&entity.YxVolunteerRecord{}, "volunteer_id = ?", volunteerID).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) BatchCreate(items []entity.YxVolunteerRecord, batchSize int) error {
|
||||||
|
return config.DB.CreateInBatches(items, batchSize).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) BatchUpdate(items []entity.YxVolunteerRecord) error {
|
||||||
|
return config.DB.Save(items).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) BatchUpsert(items []entity.YxVolunteerRecord, updateColumns []string) error {
|
||||||
|
return config.DB.Clauses(clause.OnConflict{
|
||||||
|
Columns: []clause.Column{{Name: "id"}},
|
||||||
|
DoUpdates: clause.AssignmentColumns(updateColumns),
|
||||||
|
}).CreateInBatches(items, 100).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *YxVolunteerRecordMapper) BatchDelete(ids []string) error {
|
||||||
|
return config.DB.Delete(&entity.YxVolunteerRecord{}, "id IN ?", ids).Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,23 +17,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxCalculationMajorService struct {
|
type YxCalculationMajorService struct {
|
||||||
*common.BaseService[entity.YxCalculationMajor]
|
|
||||||
historyMajorEnrollService *YxHistoryMajorEnrollService
|
historyMajorEnrollService *YxHistoryMajorEnrollService
|
||||||
mapper *mapper.YxCalculationMajorMapper
|
mapper *mapper.YxCalculationMajorMapper
|
||||||
historyScoreControlLineService *YxHistoryScoreControlLineService
|
historyScoreControlLineService *YxHistoryScoreControlLineService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxCalculationMajorService() *YxCalculationMajorService {
|
|
||||||
mapper := mapper.NewYxCalculationMajorMapper()
|
|
||||||
return &YxCalculationMajorService{
|
|
||||||
BaseService: common.NewBaseService[entity.YxCalculationMajor](),
|
|
||||||
historyMajorEnrollService: NewYxHistoryMajorEnrollService(),
|
|
||||||
mapper: mapper,
|
|
||||||
historyScoreControlLineService: NewYxHistoryScoreControlLineService(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecommendMajorList 推荐专业列表
|
|
||||||
func (s *YxCalculationMajorService) RecommendMajorList(schoolMajorQuery yxDto.SchoolMajorQuery) ([]yxDto.UserMajorDTO, int64, dto.ProbabilityCountDTO, error) {
|
func (s *YxCalculationMajorService) RecommendMajorList(schoolMajorQuery yxDto.SchoolMajorQuery) ([]yxDto.UserMajorDTO, int64, dto.ProbabilityCountDTO, error) {
|
||||||
if schoolMajorQuery.UserScoreVO.ProfessionalCategory == "" {
|
if schoolMajorQuery.UserScoreVO.ProfessionalCategory == "" {
|
||||||
return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("专业类型错误")
|
return nil, 0, dto.ProbabilityCountDTO{}, fmt.Errorf("专业类型错误")
|
||||||
|
|
@ -77,7 +65,6 @@ func (s *YxCalculationMajorService) RecommendMajorList(schoolMajorQuery yxDto.Sc
|
||||||
return calculationMajors, total, probCount, nil
|
return calculationMajors, total, probCount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchCreateBySchoolMajorDTO 根据专业 DTO 批量创建
|
|
||||||
func (s *YxCalculationMajorService) BatchCreateBySchoolMajorDTO(tableName string, items []dto.SchoolMajorDTO, scoreID string) error {
|
func (s *YxCalculationMajorService) BatchCreateBySchoolMajorDTO(tableName string, items []dto.SchoolMajorDTO, scoreID string) error {
|
||||||
entities := make([]entity.YxCalculationMajor, 0, len(items))
|
entities := make([]entity.YxCalculationMajor, 0, len(items))
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
@ -134,22 +121,51 @@ func (s *YxCalculationMajorService) BatchCreateBySchoolMajorDTO(tableName string
|
||||||
return s.BatchCreate(tableName, entities)
|
return s.BatchCreate(tableName, entities)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByScoreID 根据 scoreID 获取计算专业列表
|
func NewYxCalculationMajorService() *YxCalculationMajorService {
|
||||||
|
return &YxCalculationMajorService{
|
||||||
|
historyMajorEnrollService: NewYxHistoryMajorEnrollService(),
|
||||||
|
mapper: mapper.NewYxCalculationMajorMapper(),
|
||||||
|
historyScoreControlLineService: NewYxHistoryScoreControlLineService(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
func (s *YxCalculationMajorService) GetByScoreID(scoreID string) ([]entity.YxCalculationMajor, error) {
|
||||||
return s.mapper.FindByScoreID(scoreID)
|
return s.mapper.FindByScoreID(scoreID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindListByCompositeKeys 根据复合键查找列表
|
|
||||||
func (s *YxCalculationMajorService) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
|
func (s *YxCalculationMajorService) FindListByCompositeKeys(tableName string, keys []string, scoreId string) ([]entity.YxCalculationMajor, error) {
|
||||||
return s.mapper.FindListByCompositeKeys(tableName, keys, scoreId)
|
return s.mapper.FindListByCompositeKeys(tableName, keys, scoreId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindDtoListByCompositeKeys 根据复合键查找 DTO 列表
|
|
||||||
func (s *YxCalculationMajorService) FindDtoListByCompositeKeys(tableName string, keys []string, scoreId string) ([]dto.SchoolMajorDTO, error) {
|
func (s *YxCalculationMajorService) FindDtoListByCompositeKeys(tableName string, keys []string, scoreId string) ([]dto.SchoolMajorDTO, error) {
|
||||||
return s.mapper.FindDtoListByCompositeKeys(tableName, keys, scoreId)
|
return s.mapper.FindDtoListByCompositeKeys(tableName, keys, scoreId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchCreate 批量创建(支持动态表名,使用 UUID 生成 ID)
|
|
||||||
func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity.YxCalculationMajor) error {
|
func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity.YxCalculationMajor) error {
|
||||||
for i := range items {
|
for i := range items {
|
||||||
items[i].ID = uuid.New().String()
|
items[i].ID = uuid.New().String()
|
||||||
|
|
@ -157,23 +173,53 @@ func (s *YxCalculationMajorService) BatchCreate(tableName string, items []entity
|
||||||
return s.mapper.BatchCreate(tableName, items, 100)
|
return s.mapper.BatchCreate(tableName, items, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByScoreID 根据 scoreID 删除
|
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 {
|
func (s *YxCalculationMajorService) DeleteByScoreID(scoreID string) error {
|
||||||
return s.mapper.DeleteByScoreID(scoreID)
|
return s.mapper.DeleteByScoreID(scoreID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByScoreIDFromTable 从指定表删除 scoreID 对应的数据
|
|
||||||
func (s *YxCalculationMajorService) DeleteByScoreIDFromTable(tableName, scoreID string) error {
|
func (s *YxCalculationMajorService) DeleteByScoreIDFromTable(tableName, scoreID string) error {
|
||||||
return s.mapper.DeleteByScoreIDFromTable(tableName, scoreID)
|
return s.mapper.DeleteByScoreIDFromTable(tableName, scoreID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByUserQueryType 根据用户查询类型获取专业列表
|
// 函数名 根据用户查询类型获取专业列表
|
||||||
|
// 详细描述(可选)
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
//
|
||||||
|
// professionalCategory - 专业分类
|
||||||
|
// cognitioPolyclinic - 文理文科
|
||||||
|
// professionalCategoryChildren - 专业分类子项
|
||||||
|
//
|
||||||
|
// 返回值说明:
|
||||||
|
//
|
||||||
|
// 返回值类型 - 返回值描述
|
||||||
func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory string, cognitioPolyclinic string,
|
func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory string, cognitioPolyclinic string,
|
||||||
professionalCategoryChildren []string) ([]dto.SchoolMajorDTO, error) {
|
professionalCategoryChildren []string) ([]dto.SchoolMajorDTO, error) {
|
||||||
// 构造查询条件
|
// 构造查询条件
|
||||||
query := dto.SchoolMajorQuery{
|
query := dto.SchoolMajorQuery{
|
||||||
MajorType: professionalCategory,
|
MajorType: professionalCategory,
|
||||||
Category: cognitioPolyclinic,
|
Category: cognitioPolyclinic,
|
||||||
|
// MainSubjects: "器乐",
|
||||||
|
// MajorTypeChildren: []string{
|
||||||
|
// "音乐教育",
|
||||||
|
// },
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行院校查询
|
// 执行院校查询
|
||||||
|
|
@ -198,13 +244,13 @@ func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory str
|
||||||
for code := range schoolCodeSet {
|
for code := range schoolCodeSet {
|
||||||
schoolCodes = append(schoolCodes, code)
|
schoolCodes = append(schoolCodes, code)
|
||||||
}
|
}
|
||||||
// 执行查询院校专业的历年数据
|
// 执行查询院校专业的历年数据 - 类似Java中的in查询
|
||||||
historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames(
|
historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames(
|
||||||
schoolCodes,
|
schoolCodes,
|
||||||
majorNames,
|
majorNames,
|
||||||
query.Category,
|
query.Category, // 文科/理科
|
||||||
query.MajorType,
|
query.MajorType, // 专业类型
|
||||||
years,
|
years, // 年份
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -225,6 +271,7 @@ func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory str
|
||||||
for _, year := range years {
|
for _, year := range years {
|
||||||
key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year
|
key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year
|
||||||
if historyItem, ok := allHistoryMajorEnrollMap[key]; ok {
|
if historyItem, ok := allHistoryMajorEnrollMap[key]; ok {
|
||||||
|
// 类型转换:entity -> dto
|
||||||
dtoItem := dto.YxHistoryMajorEnrollDTO{
|
dtoItem := dto.YxHistoryMajorEnrollDTO{
|
||||||
Year: historyItem.Year,
|
Year: historyItem.Year,
|
||||||
EnrollmentCode: historyItem.EnrollmentCode,
|
EnrollmentCode: historyItem.EnrollmentCode,
|
||||||
|
|
@ -232,6 +279,7 @@ func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory str
|
||||||
ProbabilityOperator: historyItem.ProbabilityOperator,
|
ProbabilityOperator: historyItem.ProbabilityOperator,
|
||||||
AdmissionLine: historyItem.AdmissionLine,
|
AdmissionLine: historyItem.AdmissionLine,
|
||||||
ControlLine: historyItem.ControlLine,
|
ControlLine: historyItem.ControlLine,
|
||||||
|
// 复制其他需要的字段
|
||||||
}
|
}
|
||||||
historyMap[year] = dtoItem
|
historyMap[year] = dtoItem
|
||||||
}
|
}
|
||||||
|
|
@ -242,7 +290,6 @@ func (s *YxCalculationMajorService) ListByUserQueryType(professionalCategory str
|
||||||
return majorItems, nil
|
return majorItems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserMajorDTOGetHistory 获取用户专业 DTO 的历史数据
|
|
||||||
func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]dto.UserMajorDTO) error {
|
func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]dto.UserMajorDTO) error {
|
||||||
if len(*userMajorDTOList) > 0 {
|
if len(*userMajorDTOList) > 0 {
|
||||||
// 分别收集专业名称和院校代码集合(去重)
|
// 分别收集专业名称和院校代码集合(去重)
|
||||||
|
|
@ -262,13 +309,13 @@ func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]d
|
||||||
for code := range schoolCodeSet {
|
for code := range schoolCodeSet {
|
||||||
schoolCodes = append(schoolCodes, code)
|
schoolCodes = append(schoolCodes, code)
|
||||||
}
|
}
|
||||||
// 执行查询院校专业的历年数据
|
// 执行查询院校专业的历年数据 - 类似Java中的in查询
|
||||||
historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames(
|
historyItems, err := s.historyMajorEnrollService.ListBySchoolCodesAndMajorNames(
|
||||||
schoolCodes,
|
schoolCodes,
|
||||||
majorNames,
|
majorNames,
|
||||||
(*userMajorDTOList)[0].Category,
|
(*userMajorDTOList)[0].Category, // 文科/理科
|
||||||
(*userMajorDTOList)[0].MajorType,
|
(*userMajorDTOList)[0].MajorType, // 专业类型
|
||||||
years,
|
years, // 年份
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -289,6 +336,7 @@ func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]d
|
||||||
for _, year := range years {
|
for _, year := range years {
|
||||||
key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year
|
key := majorItem.SchoolCode + "_" + majorItem.MajorName + "_" + majorItem.Batch + "_" + year
|
||||||
if historyItem, ok := allHistoryMajorEnrollMap[key]; ok {
|
if historyItem, ok := allHistoryMajorEnrollMap[key]; ok {
|
||||||
|
// 类型转换:entity -> dto
|
||||||
dtoItem := dto.YxHistoryMajorEnrollDTO{
|
dtoItem := dto.YxHistoryMajorEnrollDTO{
|
||||||
Year: historyItem.Year,
|
Year: historyItem.Year,
|
||||||
EnrollmentCode: historyItem.EnrollmentCode,
|
EnrollmentCode: historyItem.EnrollmentCode,
|
||||||
|
|
@ -296,6 +344,7 @@ func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]d
|
||||||
ProbabilityOperator: historyItem.ProbabilityOperator,
|
ProbabilityOperator: historyItem.ProbabilityOperator,
|
||||||
AdmissionLine: historyItem.AdmissionLine,
|
AdmissionLine: historyItem.AdmissionLine,
|
||||||
ControlLine: historyItem.ControlLine,
|
ControlLine: historyItem.ControlLine,
|
||||||
|
// 复制其他需要的字段
|
||||||
}
|
}
|
||||||
historyMap[year] = dtoItem
|
historyMap[year] = dtoItem
|
||||||
}
|
}
|
||||||
|
|
@ -306,8 +355,21 @@ func (s *YxCalculationMajorService) UserMajorDTOGetHistory(userMajorDTOList *[]d
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckEnrollProbability 计算专业列表录取率
|
// 函数名 给专业列表计算录取率
|
||||||
|
// 详细描述(可选)
|
||||||
|
//
|
||||||
|
// 参数说明:
|
||||||
|
//
|
||||||
|
// schoolMajorDTOList - 专业列表
|
||||||
|
// professionalCategory - 专业分类
|
||||||
|
// cognitioPolyclinic - 文理文科
|
||||||
|
// professionalCategoryChildren - 专业分类子项
|
||||||
|
//
|
||||||
|
// 返回值说明:
|
||||||
|
//
|
||||||
|
// 返回值类型 - 返回值描述
|
||||||
func (s *YxCalculationMajorService) CheckEnrollProbability(schoolMajorDTOList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) error {
|
func (s *YxCalculationMajorService) CheckEnrollProbability(schoolMajorDTOList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) error {
|
||||||
|
|
||||||
professionalCategory := userScoreVO.ProfessionalCategory
|
professionalCategory := userScoreVO.ProfessionalCategory
|
||||||
if "表演类" == professionalCategory {
|
if "表演类" == professionalCategory {
|
||||||
// TODO: biaoyanService
|
// TODO: biaoyanService
|
||||||
|
|
@ -316,20 +378,25 @@ func (s *YxCalculationMajorService) CheckEnrollProbability(schoolMajorDTOList *[
|
||||||
} else {
|
} else {
|
||||||
s.betaRecommendMajorListSetEnrollProbability(schoolMajorDTOList, userScoreVO)
|
s.betaRecommendMajorListSetEnrollProbability(schoolMajorDTOList, userScoreVO)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// betaRecommendMajorListSetEnrollProbability 美术与设计类,书法类,体育类 获取录取率
|
// betaRecommendMajorListSetEnrollProbability 美术与设计类,书法类,体育类 按这个走 获取录取率
|
||||||
func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(recommendMajorList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) {
|
func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(recommendMajorList *[]dto.SchoolMajorDTO, userScoreVO vo.UserScoreVO) {
|
||||||
if recommendMajorList == nil || len(*recommendMajorList) == 0 {
|
if recommendMajorList == nil || len(*recommendMajorList) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
professionalCategory := userScoreVO.ProfessionalCategory
|
professionalCategory := userScoreVO.ProfessionalCategory
|
||||||
nowBatch := "本科"
|
nowBatch := "本科" // TODO 默认为本科,需根据实际情况获取,Java中是从 userScore 获取,这里 VO 中似乎没有 Batch? 假设为本科或从 VO 获取
|
||||||
|
// userScoreVO.Batch? 检查 VO 定义
|
||||||
|
// 假设 userScoreVO 没有 Batch,需要确认。Entity YxUserScore 有 Batch。VO 可能需要补充。
|
||||||
|
// 暂时假设为 "本科" 或 ""
|
||||||
|
|
||||||
// 获取省控线 Map
|
// 获取省控线 Map
|
||||||
historyScoreControlLineMap, err := s.historyScoreControlLineService.MapsBatchByProfessionalCategoryOfYear(common.NowYear, professionalCategory, userScoreVO.CognitioPolyclinic)
|
historyScoreControlLineMap, err := s.historyScoreControlLineService.MapsBatchByProfessionalCategoryOfYear(common.NowYear, professionalCategory, userScoreVO.CognitioPolyclinic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Log error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,10 +412,11 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
// 获取对应批次的省控线
|
// 获取对应批次的省控线
|
||||||
controlLineData, ok := historyScoreControlLineMap[item.Batch]
|
controlLineData, ok := historyScoreControlLineMap[item.Batch]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
// 尝试默认批次
|
||||||
if val, okDefault := historyScoreControlLineMap["本科"]; okDefault {
|
if val, okDefault := historyScoreControlLineMap["本科"]; okDefault {
|
||||||
controlLineData = val
|
controlLineData = val
|
||||||
} else {
|
} else {
|
||||||
continue
|
continue // 没有省控线无法计算
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,7 +445,7 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 25年专业录取原则变动 (体育类特殊逻辑)
|
// 25年专业录取原则变动 (体育类特殊逻辑,硬编码 ID 列表)
|
||||||
if item.MajorType == "体育类" {
|
if item.MajorType == "体育类" {
|
||||||
specialSchoolCodes := []string{"6530", "6085", "6110", "6065", "6050"}
|
specialSchoolCodes := []string{"6530", "6085", "6110", "6065", "6050"}
|
||||||
isSpecial := false
|
isSpecial := false
|
||||||
|
|
@ -402,7 +470,7 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
// 计算学生折合分
|
// 计算学生折合分
|
||||||
studentScore := calc.ConvertIntoScore(rulesEnrollProbability, culturalScore, professionalScore, probabilityOperator)
|
studentScore := calc.ConvertIntoScore(rulesEnrollProbability, culturalScore, professionalScore, probabilityOperator)
|
||||||
item.PrivateStudentScore = studentScore
|
item.PrivateStudentScore = studentScore
|
||||||
item.StudentScore = studentScore
|
item.StudentScore = studentScore // 展示用
|
||||||
|
|
||||||
// 权限检查
|
// 权限检查
|
||||||
if !calc.HasComputeEnrollProbabilityPermissions(nowBatch, item.Batch) {
|
if !calc.HasComputeEnrollProbabilityPermissions(nowBatch, item.Batch) {
|
||||||
|
|
@ -422,12 +490,14 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
// 当前年省控线 折合后
|
||||||
nowYearProvincialControlLine := calc.ConvertIntoScore(rulesEnrollProbability, culturalControlLine, specialControlLine, probabilityOperator)
|
nowYearProvincialControlLine := calc.ConvertIntoScore(rulesEnrollProbability, culturalControlLine, specialControlLine, probabilityOperator)
|
||||||
if nowYearProvincialControlLine <= 0 {
|
if nowYearProvincialControlLine <= 0 {
|
||||||
item.EnrollProbability = common.Number0
|
item.EnrollProbability = common.Number0
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 历年分差
|
||||||
diffMap := calc.ComputeHistoryMajorEnrollScoreLineDifferenceWithRulesEnrollProbability(item.MajorType, rulesEnrollProbability, probabilityOperator, item.HistoryMajorEnrollMap)
|
diffMap := calc.ComputeHistoryMajorEnrollScoreLineDifferenceWithRulesEnrollProbability(item.MajorType, rulesEnrollProbability, probabilityOperator, item.HistoryMajorEnrollMap)
|
||||||
historyThreeYearDiff := diffMap["scoreDifference"].(float64)
|
historyThreeYearDiff := diffMap["scoreDifference"].(float64)
|
||||||
|
|
||||||
|
|
@ -436,10 +506,14 @@ func (s *YxCalculationMajorService) betaRecommendMajorListSetEnrollProbability(r
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当前年线差
|
||||||
nowYearDiff := studentScore - nowYearProvincialControlLine
|
nowYearDiff := studentScore - nowYearProvincialControlLine
|
||||||
|
|
||||||
|
// 计算录取率
|
||||||
enrollProbability := calc.CommonCheckEnrollProbability(nowYearDiff, historyThreeYearDiff)
|
enrollProbability := calc.CommonCheckEnrollProbability(nowYearDiff, historyThreeYearDiff)
|
||||||
item.EnrollProbability = calc.CommonCheckEnrollProbabilityBeilv(enrollProbability)
|
item.EnrollProbability = calc.CommonCheckEnrollProbabilityBeilv(enrollProbability)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log time...
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
|
||||||
"server/config"
|
"server/config"
|
||||||
"server/modules/yx/dto"
|
"server/modules/yx/dto"
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
|
|
@ -12,18 +11,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxHistoryMajorEnrollService struct {
|
type YxHistoryMajorEnrollService struct {
|
||||||
*common.BaseService[entity.YxHistoryMajorEnroll]
|
|
||||||
mapper *mapper.YxHistoryMajorEnrollMapper
|
mapper *mapper.YxHistoryMajorEnrollMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxHistoryMajorEnrollService() *YxHistoryMajorEnrollService {
|
|
||||||
mapper := mapper.NewYxHistoryMajorEnrollMapper()
|
|
||||||
return &YxHistoryMajorEnrollService{
|
|
||||||
BaseService: common.NewBaseService[entity.YxHistoryMajorEnroll](),
|
|
||||||
mapper: mapper,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecommendMajorDTOListSetHistoryInfo 填充历史录取信息
|
// RecommendMajorDTOListSetHistoryInfo 填充历史录取信息
|
||||||
func (s *YxHistoryMajorEnrollService) RecommendMajorDTOListSetHistoryInfo(dtoList *[]dto.SchoolMajorDTO, years []string) {
|
func (s *YxHistoryMajorEnrollService) RecommendMajorDTOListSetHistoryInfo(dtoList *[]dto.SchoolMajorDTO, years []string) {
|
||||||
if dtoList == nil || len(*dtoList) == 0 {
|
if dtoList == nil || len(*dtoList) == 0 {
|
||||||
|
|
@ -61,7 +51,7 @@ func (s *YxHistoryMajorEnrollService) RecommendMajorDTOListSetHistoryInfo(dtoLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListBySchoolCodesAndMajorNames 根据学校代码和专业名称查询历史招生数据
|
// HistoryMajorEnrollService 中的方法
|
||||||
func (s *YxHistoryMajorEnrollService) ListBySchoolCodesAndMajorNames(
|
func (s *YxHistoryMajorEnrollService) ListBySchoolCodesAndMajorNames(
|
||||||
schoolCodes []string,
|
schoolCodes []string,
|
||||||
majorNames []string,
|
majorNames []string,
|
||||||
|
|
@ -101,18 +91,50 @@ func (s *YxHistoryMajorEnrollService) ListBySchoolCodesAndMajorNames(
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByYear 根据年份查询历史招生数据
|
func NewYxHistoryMajorEnrollService() *YxHistoryMajorEnrollService {
|
||||||
func (s *YxHistoryMajorEnrollService) GetByYear(year string) ([]entity.YxHistoryMajorEnroll, error) {
|
return &YxHistoryMajorEnrollService{mapper: mapper.NewYxHistoryMajorEnrollMapper()}
|
||||||
return s.mapper.FindByYear(year)
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建历史招生数据(使用 UUID 生成 ID)
|
|
||||||
func (s *YxHistoryMajorEnrollService) Create(item *entity.YxHistoryMajorEnroll) error {
|
func (s *YxHistoryMajorEnrollService) Create(item *entity.YxHistoryMajorEnroll) error {
|
||||||
item.ID = uuid.New().String()
|
item.ID = uuid.New().String()
|
||||||
return s.mapper.Create(item)
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新(使用 UUID 生成 ID)
|
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 {
|
func (s *YxHistoryMajorEnrollService) BatchUpsert(items []entity.YxHistoryMajorEnroll, updateColumns []string) error {
|
||||||
for i := range items {
|
for i := range items {
|
||||||
if items[i].ID == "" {
|
if items[i].ID == "" {
|
||||||
|
|
@ -121,3 +143,7 @@ func (s *YxHistoryMajorEnrollService) BatchUpsert(items []entity.YxHistoryMajorE
|
||||||
}
|
}
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
return s.mapper.BatchUpsert(items, updateColumns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *YxHistoryMajorEnrollService) BatchDelete(ids []string) error {
|
||||||
|
return s.mapper.BatchDelete(ids)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
|
|
||||||
|
|
@ -10,25 +9,49 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxSchoolMajorService struct {
|
type YxSchoolMajorService struct {
|
||||||
*common.BaseService[entity.YxSchoolMajor]
|
|
||||||
mapper *mapper.YxSchoolMajorMapper
|
mapper *mapper.YxSchoolMajorMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxSchoolMajorService() *YxSchoolMajorService {
|
func NewYxSchoolMajorService() *YxSchoolMajorService {
|
||||||
mapper := mapper.NewYxSchoolMajorMapper()
|
return &YxSchoolMajorService{mapper: mapper.NewYxSchoolMajorMapper()}
|
||||||
return &YxSchoolMajorService{
|
}
|
||||||
BaseService: common.NewBaseService[entity.YxSchoolMajor](),
|
|
||||||
mapper: mapper,
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建院校专业(使用 UUID 生成 ID)
|
|
||||||
func (s *YxSchoolMajorService) Create(item *entity.YxSchoolMajor) error {
|
func (s *YxSchoolMajorService) Create(item *entity.YxSchoolMajor) error {
|
||||||
item.ID = uuid.New().String()
|
item.ID = uuid.New().String()
|
||||||
return s.mapper.Create(item)
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新(使用 UUID 生成 ID)
|
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 {
|
func (s *YxSchoolMajorService) BatchUpsert(items []entity.YxSchoolMajor, updateColumns []string) error {
|
||||||
for i := range items {
|
for i := range items {
|
||||||
if items[i].ID == "" {
|
if items[i].ID == "" {
|
||||||
|
|
@ -37,3 +60,7 @@ func (s *YxSchoolMajorService) BatchUpsert(items []entity.YxSchoolMajor, updateC
|
||||||
}
|
}
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
return s.mapper.BatchUpsert(items, updateColumns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *YxSchoolMajorService) BatchDelete(ids []string) error {
|
||||||
|
return s.mapper.BatchDelete(ids)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
|
|
||||||
|
|
@ -10,30 +9,53 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxUserScoreService struct {
|
type YxUserScoreService struct {
|
||||||
*common.BaseService[entity.YxUserScore]
|
|
||||||
mapper *mapper.YxUserScoreMapper
|
mapper *mapper.YxUserScoreMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxUserScoreService() *YxUserScoreService {
|
func NewYxUserScoreService() *YxUserScoreService {
|
||||||
mapper := mapper.NewYxUserScoreMapper()
|
return &YxUserScoreService{mapper: mapper.NewYxUserScoreMapper()}
|
||||||
return &YxUserScoreService{
|
|
||||||
BaseService: common.NewBaseService[entity.YxUserScore](),
|
|
||||||
mapper: mapper,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFieldsByEntity 根据实体条件更新字段
|
func (s *YxUserScoreService) List(page, size int) ([]entity.YxUserScore, int64, error) {
|
||||||
func (s *YxUserScoreService) UpdateFieldsByEntity(item *entity.YxUserScore, fields map[string]interface{}) error {
|
return s.mapper.FindAll(page, size)
|
||||||
return s.mapper.UpdateFieldsByMultiCondition(item, fields)
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) GetByID(id string) (*entity.YxUserScore, error) {
|
||||||
|
return s.mapper.FindByID(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建用户成绩(使用 UUID 生成 ID)
|
|
||||||
func (s *YxUserScoreService) Create(item *entity.YxUserScore) error {
|
func (s *YxUserScoreService) Create(item *entity.YxUserScore) error {
|
||||||
item.ID = uuid.New().String()
|
item.ID = uuid.New().String()
|
||||||
return s.mapper.Create(item)
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新(使用 UUID 生成 ID)
|
func (s *YxUserScoreService) Update(item *entity.YxUserScore) error {
|
||||||
|
return s.mapper.Update(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return s.mapper.UpdateFields(id, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) UpdateFieldsByEntity(item *entity.YxUserScore, fields map[string]interface{}) error {
|
||||||
|
return s.mapper.UpdateFieldsByMultiCondition(item, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) Delete(id string) error {
|
||||||
|
return s.mapper.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) BatchCreate(items []entity.YxUserScore) error {
|
||||||
|
for i := range items {
|
||||||
|
items[i].ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return s.mapper.BatchCreate(items, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) BatchUpdate(items []entity.YxUserScore) error {
|
||||||
|
return s.mapper.BatchUpdate(items)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *YxUserScoreService) BatchUpsert(items []entity.YxUserScore, updateColumns []string) error {
|
func (s *YxUserScoreService) BatchUpsert(items []entity.YxUserScore, updateColumns []string) error {
|
||||||
for i := range items {
|
for i := range items {
|
||||||
if items[i].ID == "" {
|
if items[i].ID == "" {
|
||||||
|
|
@ -42,3 +64,7 @@ func (s *YxUserScoreService) BatchUpsert(items []entity.YxUserScore, updateColum
|
||||||
}
|
}
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
return s.mapper.BatchUpsert(items, updateColumns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *YxUserScoreService) BatchDelete(ids []string) error {
|
||||||
|
return s.mapper.BatchDelete(ids)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"server/common"
|
|
||||||
"server/modules/yx/entity"
|
"server/modules/yx/entity"
|
||||||
"server/modules/yx/mapper"
|
"server/modules/yx/mapper"
|
||||||
|
|
||||||
|
|
@ -10,35 +9,57 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type YxVolunteerRecordService struct {
|
type YxVolunteerRecordService struct {
|
||||||
*common.BaseService[entity.YxVolunteerRecord]
|
|
||||||
mapper *mapper.YxVolunteerRecordMapper
|
mapper *mapper.YxVolunteerRecordMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxVolunteerRecordService() *YxVolunteerRecordService {
|
func NewYxVolunteerRecordService() *YxVolunteerRecordService {
|
||||||
mapper := mapper.NewYxVolunteerRecordMapper()
|
return &YxVolunteerRecordService{mapper: mapper.NewYxVolunteerRecordMapper()}
|
||||||
return &YxVolunteerRecordService{
|
}
|
||||||
BaseService: common.NewBaseService[entity.YxVolunteerRecord](),
|
|
||||||
mapper: mapper,
|
func (s *YxVolunteerRecordService) List(page, size int) ([]entity.YxVolunteerRecord, int64, error) {
|
||||||
}
|
return s.mapper.FindAll(page, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByVolunteerID 根据 volunteerID 查找志愿记录
|
|
||||||
func (s *YxVolunteerRecordService) FindByVolunteerID(volunteerID string) ([]entity.YxVolunteerRecord, error) {
|
func (s *YxVolunteerRecordService) FindByVolunteerID(volunteerID string) ([]entity.YxVolunteerRecord, error) {
|
||||||
return s.mapper.FindByVolunteerID(volunteerID)
|
return s.mapper.FindByVolunteerID(volunteerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteByVolunteerID 根据 volunteerID 删除志愿记录
|
func (s *YxVolunteerRecordService) GetByID(id string) (*entity.YxVolunteerRecord, error) {
|
||||||
func (s *YxVolunteerRecordService) DeleteByVolunteerID(volunteerID string) error {
|
return s.mapper.FindByID(id)
|
||||||
return s.mapper.DeleteByVolunteerID(volunteerID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 创建志愿记录(使用 UUID 生成 ID)
|
|
||||||
func (s *YxVolunteerRecordService) Create(item *entity.YxVolunteerRecord) error {
|
func (s *YxVolunteerRecordService) Create(item *entity.YxVolunteerRecord) error {
|
||||||
item.ID = uuid.New().String()
|
item.ID = uuid.New().String()
|
||||||
return s.mapper.Create(item)
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchUpsert 批量插入或更新(使用 UUID 生成 ID)
|
func (s *YxVolunteerRecordService) Update(item *entity.YxVolunteerRecord) error {
|
||||||
|
return s.mapper.Update(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return s.mapper.UpdateFields(id, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) Delete(id string) error {
|
||||||
|
return s.mapper.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) DeleteByVolunteerID(volunteerID string) error {
|
||||||
|
return s.mapper.DeleteByVolunteerID(volunteerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) BatchCreate(items []entity.YxVolunteerRecord) error {
|
||||||
|
for i := range items {
|
||||||
|
items[i].ID = uuid.New().String()
|
||||||
|
}
|
||||||
|
return s.mapper.BatchCreate(items, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) BatchUpdate(items []entity.YxVolunteerRecord) error {
|
||||||
|
return s.mapper.BatchUpdate(items)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *YxVolunteerRecordService) BatchUpsert(items []entity.YxVolunteerRecord, updateColumns []string) error {
|
func (s *YxVolunteerRecordService) BatchUpsert(items []entity.YxVolunteerRecord, updateColumns []string) error {
|
||||||
for i := range items {
|
for i := range items {
|
||||||
if items[i].ID == "" {
|
if items[i].ID == "" {
|
||||||
|
|
@ -47,3 +68,7 @@ func (s *YxVolunteerRecordService) BatchUpsert(items []entity.YxVolunteerRecord,
|
||||||
}
|
}
|
||||||
return s.mapper.BatchUpsert(items, updateColumns)
|
return s.mapper.BatchUpsert(items, updateColumns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerRecordService) BatchDelete(ids []string) error {
|
||||||
|
return s.mapper.BatchDelete(ids)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,25 +20,68 @@ type ScoreService interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type YxVolunteerService struct {
|
type YxVolunteerService struct {
|
||||||
*common.BaseService[entity.YxVolunteer]
|
|
||||||
mapper *mapper.YxVolunteerMapper
|
mapper *mapper.YxVolunteerMapper
|
||||||
volunteerRecordMapper *mapper.YxVolunteerRecordMapper
|
volunteerRecordMapper *mapper.YxVolunteerRecordMapper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewYxVolunteerService() *YxVolunteerService {
|
func NewYxVolunteerService() *YxVolunteerService {
|
||||||
return &YxVolunteerService{
|
return &YxVolunteerService{mapper: mapper.NewYxVolunteerMapper(), volunteerRecordMapper: mapper.NewYxVolunteerRecordMapper()}
|
||||||
BaseService: common.NewBaseService[entity.YxVolunteer](),
|
}
|
||||||
mapper: mapper.NewYxVolunteerMapper(),
|
|
||||||
volunteerRecordMapper: mapper.NewYxVolunteerRecordMapper(),
|
func (s *YxVolunteerService) List(page, size int) ([]entity.YxVolunteer, int64, error) {
|
||||||
}
|
return s.mapper.FindAll(page, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) GetByID(id string) (*entity.YxVolunteer, error) {
|
||||||
|
return s.mapper.FindByID(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) Create(item *entity.YxVolunteer) error {
|
||||||
|
item.ID = common.GenerateStringID()
|
||||||
|
return s.mapper.Create(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindActiveByScoreId 根据 scoreId 查找激活的志愿单
|
|
||||||
func (s *YxVolunteerService) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
|
func (s *YxVolunteerService) FindActiveByScoreId(scoreId string) (*entity.YxVolunteer, error) {
|
||||||
return s.mapper.FindActiveByScoreId(scoreId)
|
return s.mapper.FindActiveByScoreId(scoreId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateByScoreId 根据 ScoreId 创建新志愿表
|
func (s *YxVolunteerService) Update(item *entity.YxVolunteer) error {
|
||||||
|
return s.mapper.Update(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) UpdateFields(id string, fields map[string]interface{}) error {
|
||||||
|
return s.mapper.UpdateFields(id, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) Delete(id string) error {
|
||||||
|
return s.mapper.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) BatchCreate(items []entity.YxVolunteer) error {
|
||||||
|
for i := range items {
|
||||||
|
items[i].ID = common.GenerateStringID()
|
||||||
|
}
|
||||||
|
return s.mapper.BatchCreate(items, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) BatchUpdate(items []entity.YxVolunteer) error {
|
||||||
|
return s.mapper.BatchUpdate(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) BatchUpsert(items []entity.YxVolunteer, updateColumns []string) error {
|
||||||
|
for i := range items {
|
||||||
|
if items[i].ID == "" {
|
||||||
|
items[i].ID = common.GenerateStringID()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.mapper.BatchUpsert(items, updateColumns)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *YxVolunteerService) BatchDelete(ids []string) error {
|
||||||
|
return s.mapper.BatchDelete(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据ScoreId创建新志愿表
|
||||||
func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) error {
|
func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) error {
|
||||||
volunteer := entity.YxVolunteer{}
|
volunteer := entity.YxVolunteer{}
|
||||||
volunteer.ID = common.GenerateStringID()
|
volunteer.ID = common.GenerateStringID()
|
||||||
|
|
@ -52,17 +95,15 @@ func (s *YxVolunteerService) CreateByScoreId(scoreId string, userId string) erro
|
||||||
volunteer.CreateTime = time.Now()
|
volunteer.CreateTime = time.Now()
|
||||||
volunteer.UpdateTime = time.Now()
|
volunteer.UpdateTime = time.Now()
|
||||||
|
|
||||||
// 先关闭当前用户其他志愿单
|
// 先关闭当前用户其他志愿单 - ✅ 检查错误
|
||||||
if err := s.mapper.CloseOtherVolunteer(userId); err != nil {
|
if err := s.mapper.CloseOtherVolunteer(userId); err != nil {
|
||||||
return fmt.Errorf("关闭其他志愿表失败: %w", err)
|
return fmt.Errorf("关闭其他志愿表失败: %w", err)
|
||||||
}
|
}
|
||||||
// 创建志愿表
|
// 创建志愿表
|
||||||
return s.mapper.Create(&volunteer)
|
return s.mapper.Create(&volunteer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateName 更新志愿表名称
|
|
||||||
func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
||||||
volunteer, err := s.GetByID(id)
|
volunteer, err := s.mapper.FindByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -72,14 +113,12 @@ func (s *YxVolunteerService) UpdateName(id, name, userID string) error {
|
||||||
return s.mapper.UpdateFields(id, map[string]interface{}{"volunteer_name": name, "update_by": userID, "update_time": time.Now()})
|
return s.mapper.UpdateFields(id, map[string]interface{}{"volunteer_name": name, "update_by": userID, "update_time": time.Now()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListByUser 查询用户的志愿单列表(包含成绩信息)
|
|
||||||
func (s *YxVolunteerService) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
func (s *YxVolunteerService) ListByUser(userID string, page, size int) ([]vo.UserVolunteerVO, int64, error) {
|
||||||
return s.mapper.ListByUser(userID, page, size)
|
return s.mapper.ListByUser(userID, page, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteVolunteer 删除志愿单
|
|
||||||
func (s *YxVolunteerService) DeleteVolunteer(id, userID string) error {
|
func (s *YxVolunteerService) DeleteVolunteer(id, userID string) error {
|
||||||
volunteer, err := s.GetByID(id)
|
volunteer, err := s.mapper.FindByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +146,6 @@ func (s *YxVolunteerService) DeleteVolunteer(id, userID string) error {
|
||||||
return nil // 暂时回退复杂逻辑
|
return nil // 暂时回退复杂逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwitchVolunteer 切换激活的志愿单
|
|
||||||
func (s *YxVolunteerService) SwitchVolunteer(id, userID string, scoreService ScoreService) error {
|
func (s *YxVolunteerService) SwitchVolunteer(id, userID string, scoreService ScoreService) error {
|
||||||
if err := s.mapper.CloseOtherVolunteer(userID); err != nil {
|
if err := s.mapper.CloseOtherVolunteer(userID); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -116,7 +154,7 @@ func (s *YxVolunteerService) SwitchVolunteer(id, userID string, scoreService Sco
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
volunteer, err := s.GetByID(id)
|
volunteer, err := s.mapper.FindByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue