Files
EasyGoAdmin_GoFrame_EleVue/internal/logic/level.go
T
2026-10-20 11:37:25 +08:00

228 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package logic
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gerror"
"easygoadmin/api/v1"
"easygoadmin/internal/consts"
"easygoadmin/internal/dao"
"easygoadmin/internal/service"
"easygoadmin/utility/excel"
"easygoadmin/utility/upload"
)
// ============================== 职级业务实现 ==============================
// sLevel 职级业务实现
// 实现 service.ILevel 接口,接口定义见 api/v1/level.go
type sLevel struct{}
func init() {
service.RegisterLevel(&sLevel{})
}
// levelSerializeMaps 枚举字段到字典编码的映射
// {field}Text 由基座自动填充
var levelSerializeMaps = map[string]string{
"status": "level_status",
}
// levelSpec 职级模块声明
//
// 采用声明式实现:查询、输出与规则均在声明中定义,方法仅做委托
var levelSpec = ModuleSpec[v1.LevelItem, v1.LevelListItem, v1.LevelDetailRes]{
Model: dao.Level.Ctx,
LikeFields: []string{"name"}, // 名称模糊(querySchemas 输入)
EqFields: []string{"status"}, // status=0 视为未筛选(零值跳过)
OrderBy: "sort, id",
SerializeMaps: levelSerializeMaps,
ToData: levelToData,
BeforeAdd: levelDefaultStatus, // status 缺省置 1
// Delete 守卫(存在性 + 用户引用检查)
BeforeDelete: levelDeleteGuard,
}
// levelToData 全量表单语义转换
// 新增与修改均提交全部字段,兼容两种入参类型
func levelToData(req any) map[string]any {
switch r := req.(type) {
case *v1.LevelAddReq:
return map[string]any{"name": r.Name, "status": r.Status, "sort": r.Sort}
case *v1.LevelUpdateReq:
return map[string]any{"name": r.Name, "status": r.Status, "sort": r.Sort}
}
panic(fmt.Sprintf("levelToData: 不支持的入参类型 %T", req))
}
// levelDefaultStatus 状态缺省处理
// 状态为零值时置为 1(正常)
func levelDefaultStatus(ctx context.Context, data map[string]any, req any) error {
if v, ok := data["status"].(int); ok && v == 0 {
data["status"] = 1
}
return nil
}
// levelDeleteGuard 删除守卫
// 校验记录存在性,存在用户引用时禁止删除
func levelDeleteGuard(ctx context.Context, id int64) error {
if n, err := dao.Level.Ctx(ctx).Where("id", id).Count(); err != nil {
return err
} else if n == 0 {
return gerror.NewCode(consts.CodeRecordNotFound)
}
if n, err := dao.User.Ctx(ctx).Where("level_id", id).Count(); err != nil {
return err
} else if n > 0 {
return gerror.NewCode(consts.CodeInUse)
}
return nil
}
// ---------- 标准方法:3行委托 ----------
// Page 职级分页
// 分页返回职级列表,并补充关联用户数与状态文本
func (s *sLevel) Page(ctx context.Context, req *v1.LevelPageReq) (res *v1.LevelPageRes, err error) {
list, total, err := levelSpec.Page(ctx, req, req.GetPageNo(), req.GetPageSize())
if err != nil {
return nil, err
}
fillUserCount(ctx, "level_id", &list)
return v1.NewPageData(req.GetPageNo(), req.GetPageSize(), total, derefSlice(list)), nil
}
// List 职级全量列表
// 返回精简结构的职级列表
func (s *sLevel) List(ctx context.Context, req *v1.LevelListReq) (res v1.LevelListRes, err error) {
list, err := levelSpec.List(ctx, req)
if err != nil {
return nil, err
}
return derefSlice(list), nil
}
// Detail 职级详情
// 按职级ID查询详情
func (s *sLevel) Detail(ctx context.Context, req *v1.LevelDetailReq) (res *v1.LevelDetailRes, err error) {
return levelSpec.Detail(ctx, req.Id, req)
}
// Add 新增职级
// 新增成功后返回新记录ID
func (s *sLevel) Add(ctx context.Context, req *v1.LevelAddReq) (res *v1.IdRes, err error) {
id, err := levelSpec.Add(ctx, req)
if err != nil {
return nil, err
}
return &v1.IdRes{Id: id}, nil
}
// Update 修改职级
// 按职级ID更新记录
func (s *sLevel) Update(ctx context.Context, req *v1.LevelUpdateReq) (res *v1.LevelUpdateRes, err error) {
err = levelSpec.Update(ctx, req.Id, req)
if err != nil {
return nil, err
}
return &v1.LevelUpdateRes{}, nil
}
// Status 职级状态
// 非标准方法,手写实现,设置职级的正常或禁用状态
func (s *sLevel) Status(ctx context.Context, req *v1.LevelStatusReq) (res *v1.LevelStatusRes, err error) {
if _, err = dao.Level.Ctx(ctx).Where("id", req.Id).Data(map[string]any{"status": req.Status}).Update(); err != nil {
return nil, err
}
return &v1.LevelStatusRes{}, nil
}
// Delete 删除职级
// 按职级ID删除单条记录
func (s *sLevel) Delete(ctx context.Context, req *v1.LevelDeleteReq) (res *v1.LevelDeleteRes, err error) {
if err = levelSpec.Delete(ctx, req.Id); err != nil {
return nil, err
}
return &v1.LevelDeleteRes{}, nil
}
// BatchDelete 批量删除职级
// 按职级ID列表批量删除记录
func (s *sLevel) BatchDelete(ctx context.Context, req *v1.LevelBatchDeleteReq) (res *v1.LevelBatchDeleteRes, err error) {
if err = levelSpec.BatchDelete(ctx, req.Ids); err != nil {
return nil, err
}
return &v1.LevelBatchDeleteRes{}, nil
}
// ---------- 非标准方法(手写实现)(Import/Export)----------
// Import 导入职级
//
// 表格列依次为名称、状态、排序,名称重复时跳过并记录原因
func (s *sLevel) Import(ctx context.Context, req *v1.LevelImportReq) (res *v1.LevelImportRes, err error) {
_, rows, err := excel.ReadSheet(req.File)
if err != nil {
return nil, err
}
res = &v1.LevelImportRes{Total: len(rows)}
tenantId := consts.GetTenantID(ctx)
for i, row := range rows {
name := cellAt(row, 0)
if name == "" {
res.Failed++
res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:名称为空", i+2))
continue
}
count, e := dao.Level.Ctx(ctx).Where("name", name).Count()
if e != nil {
return nil, e
}
if count > 0 {
res.Failed++
res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:名称已存在", i+2))
continue
}
status := cellInt(row, 1, 1)
if status <= 0 {
status = 1
}
if _, e = dao.Level.Ctx(ctx).Data(map[string]any{
"name": name,
"status": status,
"sort": cellInt(row, 2, 0),
"tenant_id": tenantId,
}).Insert(); e != nil {
res.Failed++
res.Reasons = append(res.Reasons, fmt.Sprintf("第%d行:写入失败 %v", i+2, e))
continue
}
res.Success++
}
return res, nil
}
// Export 导出职级
// 按查询条件导出职级数据并返回文件访问地址
func (s *sLevel) Export(ctx context.Context, req *v1.LevelExportReq) (res *v1.LevelExportRes, err error) {
m := levelSpec.applyFilters(ctx, levelSpec.Model(ctx), req)
rows, err := m.Order("sort, id").All()
if err != nil {
return nil, err
}
headers := []string{"名称", "状态", "排序"}
data := make([][]any, 0, len(rows))
for _, r := range rows {
data = append(data, []any{r["name"].String(), userStatusText(r["status"].Int()), r["sort"].Int()})
}
fileName, _, err := excel.WriteSheet(upload.BaseDir(ctx), "职级数据", headers, data)
if err != nil {
return nil, err
}
url := upload.GetFileUrl(ctx, fileName)
return &url, nil
}