迁移
This commit is contained in:
@@ -0,0 +1,377 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
type AccessService struct{ uc *biz.AccessUsecase }
|
||||
|
||||
func NewAccessService(uc *biz.AccessUsecase) *AccessService { return &AccessService{uc: uc} }
|
||||
func (s *AccessService) Authorize(ctx context.Context, aid uint, path, method string) (bool, error) {
|
||||
return s.uc.Authorize(ctx, aid, path, method)
|
||||
}
|
||||
|
||||
func authorityDTO(v *biz.Authority) map[string]any { return convertAuthority(*v) }
|
||||
func apiDTO(v *biz.API) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "path": v.Path, "description": v.Description, "apiGroup": v.APIGroup, "method": v.Method}
|
||||
}
|
||||
func departmentDTO(v *biz.Department) map[string]any {
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, c := range v.Children {
|
||||
children = append(children, departmentDTO(c))
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "parentId": v.ParentID, "ancestors": v.Ancestors, "sort": v.Sort, "leaderId": v.LeaderID, "leader": nil, "status": v.Status, "children": children, "namePath": ""}
|
||||
}
|
||||
func positionDTO(v *biz.Position) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "code": v.Code, "sort": v.Sort, "status": v.Status, "remark": v.Remark}
|
||||
}
|
||||
|
||||
func (s *AccessService) Authorities(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListAuthorities(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byID := make(map[uint]map[string]any, len(items))
|
||||
for _, v := range items {
|
||||
byID[v.AuthorityID] = authorityDTO(v)
|
||||
}
|
||||
roots := make([]map[string]any, 0)
|
||||
for _, v := range items {
|
||||
item := byID[v.AuthorityID]
|
||||
if v.ParentID != nil && *v.ParentID != 0 && byID[*v.ParentID] != nil {
|
||||
parent := byID[*v.ParentID]
|
||||
children, _ := parent["children"].([]map[string]any)
|
||||
parent["children"] = append(children, item)
|
||||
} else {
|
||||
roots = append(roots, item)
|
||||
}
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
func (s *AccessService) CreateAuthority(ctx context.Context, v *biz.Authority) error {
|
||||
return s.uc.Repo().CreateAuthority(ctx, v)
|
||||
}
|
||||
func (s *AccessService) CopyAuthority(ctx context.Context, sourceID uint, v *biz.Authority) error {
|
||||
return s.uc.Repo().CopyAuthority(ctx, sourceID, v)
|
||||
}
|
||||
func (s *AccessService) UpdateAuthority(ctx context.Context, v *biz.Authority) error {
|
||||
return s.uc.Repo().UpdateAuthority(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeleteAuthority(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteAuthority(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetAuthorityMenus(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetAuthorityMenus(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) AuthorityMenuIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().AuthorityMenuIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) AuthorityMenus(ctx context.Context, id uint) ([]map[string]any, error) {
|
||||
ids, err := s.uc.Repo().AuthorityMenuIDs(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selected := make(map[uint]bool, len(ids))
|
||||
for _, value := range ids {
|
||||
selected[value] = true
|
||||
}
|
||||
menus, err := s.uc.Repo().ListMenus(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(ids))
|
||||
for _, menu := range menus {
|
||||
if selected[menu.ID] {
|
||||
out = append(out, map[string]any{"menuId": menu.ID, "authorityId": id, "sysBaseMenu": convertMenu(menu), "parentId": menu.ParentID})
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetAuthorityUsers(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().AuthorityUserIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetDataScope(ctx context.Context, id uint, scope int, ids []uint) error {
|
||||
return s.uc.Repo().SetDataScope(ctx, id, scope, ids)
|
||||
}
|
||||
func (s *AccessService) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().DataScopeDepartmentIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
|
||||
return s.uc.Repo().ResolveDataScope(ctx, authorityID, userID)
|
||||
}
|
||||
|
||||
func (s *AccessService) Menus(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListMenus(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
v.Buttons, err = s.uc.Repo().Buttons(ctx, v.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, convertMenu(v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) MenuTree(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListMenus(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
byID := make(map[uint]*biz.Menu, len(items))
|
||||
for _, item := range items {
|
||||
item.Buttons, err = s.uc.Repo().Buttons(ctx, item.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Children = []*biz.Menu{}
|
||||
byID[item.ID] = item
|
||||
}
|
||||
roots := make([]*biz.Menu, 0)
|
||||
for _, item := range items {
|
||||
if parent := byID[item.ParentID]; parent != nil {
|
||||
parent.Children = append(parent.Children, item)
|
||||
} else {
|
||||
roots = append(roots, item)
|
||||
}
|
||||
}
|
||||
out := make([]map[string]any, 0, len(roots))
|
||||
for _, item := range roots {
|
||||
out = append(out, convertMenu(item))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) CreateMenu(ctx context.Context, v *biz.Menu) error {
|
||||
return s.uc.Repo().CreateMenu(ctx, v)
|
||||
}
|
||||
func (s *AccessService) UpdateMenu(ctx context.Context, v *biz.Menu) error {
|
||||
return s.uc.Repo().UpdateMenu(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeleteMenu(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteMenu(ctx, id)
|
||||
}
|
||||
func (s *AccessService) Menu(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindMenu(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Buttons, err = s.uc.Repo().Buttons(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertMenu(v), nil
|
||||
}
|
||||
func (s *AccessService) MenuRoleIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().MenuRoleIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetMenuRoles(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetMenuRoles(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) ReplaceMenuButtons(ctx context.Context, menuID uint, buttons []*biz.MenuButton) error {
|
||||
return s.uc.Repo().ReplaceMenuButtons(ctx, menuID, buttons)
|
||||
}
|
||||
|
||||
func (s *AccessService) APIs(ctx context.Context, page, size int, q *biz.API) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListAPIs(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, apiDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AccessService) CreateAPI(ctx context.Context, v *biz.API) error {
|
||||
return s.uc.Repo().CreateAPI(ctx, v)
|
||||
}
|
||||
func (s *AccessService) UpdateAPI(ctx context.Context, v *biz.API) error {
|
||||
return s.uc.Repo().UpdateAPI(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeleteAPIs(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteAPIs(ctx, ids)
|
||||
}
|
||||
func (s *AccessService) API(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindAPI(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apiDTO(v), nil
|
||||
}
|
||||
func (s *AccessService) APIRoleIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().APIRoleIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetAPIRoles(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetAPIRoles(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) PolicyPaths(ctx context.Context, id uint) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().PolicyPaths(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"path": v.Path, "method": v.Method})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) SetPolicyPaths(ctx context.Context, id uint, paths []*biz.API) error {
|
||||
return s.uc.Repo().SetPolicyPaths(ctx, id, paths)
|
||||
}
|
||||
func (s *AccessService) SyncAPIs(ctx context.Context, routes []*biz.API) (map[string]any, error) {
|
||||
stored, _, err := s.uc.Repo().ListAPIs(ctx, 1, 100000, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ignored, err := s.uc.Repo().IgnoredAPIs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := func(v *biz.API) string { return strings.ToUpper(v.Method) + " " + v.Path }
|
||||
ignoreSet := make(map[string]bool, len(ignored))
|
||||
for _, item := range ignored {
|
||||
ignoreSet[key(item)] = true
|
||||
}
|
||||
routeSet := make(map[string]*biz.API, len(routes))
|
||||
for _, item := range routes {
|
||||
if !ignoreSet[key(item)] {
|
||||
routeSet[key(item)] = item
|
||||
}
|
||||
}
|
||||
storedSet := make(map[string]*biz.API, len(stored))
|
||||
for _, item := range stored {
|
||||
storedSet[key(item)] = item
|
||||
}
|
||||
added := make([]map[string]any, 0)
|
||||
deleted := make([]map[string]any, 0)
|
||||
ignoredDTO := make([]map[string]any, 0, len(ignored))
|
||||
for k, item := range routeSet {
|
||||
if storedSet[k] == nil {
|
||||
added = append(added, apiDTO(item))
|
||||
}
|
||||
}
|
||||
for k, item := range storedSet {
|
||||
if routeSet[k] == nil && !ignoreSet[k] {
|
||||
deleted = append(deleted, apiDTO(item))
|
||||
}
|
||||
}
|
||||
for _, item := range ignored {
|
||||
ignoredDTO = append(ignoredDTO, apiDTO(item))
|
||||
}
|
||||
return map[string]any{"newApis": added, "deleteApis": deleted, "ignoreApis": ignoredDTO}, nil
|
||||
}
|
||||
func (s *AccessService) SetAPIIgnored(ctx context.Context, path, method string, ignored bool) error {
|
||||
return s.uc.Repo().SetAPIIgnored(ctx, path, method, ignored)
|
||||
}
|
||||
func (s *AccessService) ApplyAPISync(ctx context.Context, added, deleted []*biz.API) error {
|
||||
return s.uc.Repo().ApplyAPISync(ctx, added, deleted)
|
||||
}
|
||||
|
||||
func (s *AccessService) Buttons(ctx context.Context, id uint) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().Buttons(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"ID": v.ID, "name": v.Name, "desc": v.Description, "sysBaseMenuID": v.MenuID})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) SetAuthorityButtons(ctx context.Context, id uint, v map[uint][]uint) error {
|
||||
return s.uc.Repo().SetAuthorityButtons(ctx, id, v)
|
||||
}
|
||||
func (s *AccessService) AuthorityButtonIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().AuthorityButtonIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) {
|
||||
return s.uc.Repo().SelectedButtons(ctx, aid, menuID)
|
||||
}
|
||||
func (s *AccessService) SetSelectedButtons(ctx context.Context, aid, menuID uint, ids []uint) error {
|
||||
return s.uc.Repo().SetSelectedButtons(ctx, aid, menuID, ids)
|
||||
}
|
||||
func (s *AccessService) CanRemoveButton(ctx context.Context, id uint) (bool, error) {
|
||||
return s.uc.Repo().CanRemoveButton(ctx, id)
|
||||
}
|
||||
|
||||
func (s *AccessService) Departments(ctx context.Context, name string) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListDepartments(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, departmentDTO(v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *AccessService) CreateDepartment(ctx context.Context, v *biz.Department) error {
|
||||
return s.uc.Repo().CreateDepartment(ctx, v)
|
||||
}
|
||||
func (s *AccessService) UpdateDepartment(ctx context.Context, v *biz.Department) error {
|
||||
return s.uc.Repo().UpdateDepartment(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeleteDepartment(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteDepartment(ctx, id)
|
||||
}
|
||||
func (s *AccessService) Department(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindDepartment(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return departmentDTO(v), nil
|
||||
}
|
||||
func (s *AccessService) DepartmentUserIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().DepartmentUserIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetDepartmentUsers(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetDepartmentUsers(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) SetUserDepartments(ctx context.Context, id uint, ids []uint, primary uint) error {
|
||||
return s.uc.Repo().SetUserDepartments(ctx, id, ids, primary)
|
||||
}
|
||||
|
||||
func (s *AccessService) Positions(ctx context.Context, page, size int, q *biz.Position) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListPositions(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, positionDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AccessService) CreatePosition(ctx context.Context, v *biz.Position) error {
|
||||
return s.uc.Repo().CreatePosition(ctx, v)
|
||||
}
|
||||
func (s *AccessService) UpdatePosition(ctx context.Context, v *biz.Position) error {
|
||||
return s.uc.Repo().UpdatePosition(ctx, v)
|
||||
}
|
||||
func (s *AccessService) DeletePosition(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeletePosition(ctx, id)
|
||||
}
|
||||
func (s *AccessService) Position(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindPosition(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return positionDTO(v), nil
|
||||
}
|
||||
func (s *AccessService) PositionUserIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
return s.uc.Repo().PositionUserIDs(ctx, id)
|
||||
}
|
||||
func (s *AccessService) SetPositionUsers(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetPositionUsers(ctx, id, ids)
|
||||
}
|
||||
func (s *AccessService) SetUserPositions(ctx context.Context, id uint, ids []uint) error {
|
||||
return s.uc.Repo().SetUserPositions(ctx, id, ids)
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
type AuditService struct{ uc *biz.AuditUsecase }
|
||||
|
||||
func NewAuditService(uc *biz.AuditUsecase) *AuditService { return &AuditService{uc: uc} }
|
||||
func (s *AuditService) RecordOperation(ctx context.Context, v *biz.OperationRecord) error {
|
||||
return s.uc.Repo().RecordOperation(ctx, v)
|
||||
}
|
||||
func (s *AuditService) RecordLogin(ctx context.Context, v *biz.LoginLog) error {
|
||||
return s.uc.Repo().RecordLogin(ctx, v)
|
||||
}
|
||||
func (s *AuditService) RecordDataAccess(ctx context.Context, v *biz.DataAccessLog) error {
|
||||
return s.uc.Repo().RecordDataAccess(ctx, v)
|
||||
}
|
||||
func opDTO(v *biz.OperationRecord) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "ip": v.IP, "method": v.Method, "path": v.Path, "status": v.Status, "latency_ms": v.LatencyMS, "agent": v.Agent, "error_message": v.ErrorMessage, "body": v.Body, "resp": v.Response, "user_id": v.UserID, "request_id": v.RequestID, "trace_id": v.TraceID, "device_id": v.DeviceID, "user": nil}
|
||||
}
|
||||
func loginDTO(v *biz.LoginLog) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "username": v.Username, "ip": v.IP, "status": v.Status, "errorMessage": v.ErrorMessage, "agent": v.Agent, "userId": v.UserID, "user": nil}
|
||||
}
|
||||
func dataAccessDTO(v *biz.DataAccessLog) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "eventType": v.EventType, "targetTable": v.TargetTable, "operation": v.Operation, "userId": v.UserID, "authorityId": v.AuthorityID, "scope": v.Scope, "requestId": v.RequestID, "method": v.Method, "path": v.Path, "detail": v.Detail}
|
||||
}
|
||||
func (s *AuditService) Operations(ctx context.Context, page, size int, q *biz.OperationRecord) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListOperations(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, opDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) Operation(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindOperation(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return opDTO(v), nil
|
||||
}
|
||||
func (s *AuditService) DeleteOperations(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteOperations(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) Logins(ctx context.Context, page, size int, q *biz.LoginLog) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListLogins(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, loginDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) Login(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindLogin(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return loginDTO(v), nil
|
||||
}
|
||||
func (s *AuditService) DeleteLogins(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteLogins(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) DataAccess(ctx context.Context, page, size int, q *biz.DataAccessLog) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListDataAccess(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, dataAccessDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *AuditService) DeleteDataAccess(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteDataAccess(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) LogDates(ctx context.Context, month string) (map[string]any, error) {
|
||||
items, err := s.uc.Repo().LogDates(ctx, month)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"date": v.Date, "fileCount": v.FileCount})
|
||||
}
|
||||
return map[string]any{"month": month, "dates": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogFiles(ctx context.Context, date string) (map[string]any, error) {
|
||||
items, err := s.uc.Repo().LogFiles(ctx, date)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"path": v.Path, "name": v.Name, "size": v.Size, "modifiedAt": v.ModifiedAt})
|
||||
}
|
||||
return map[string]any{"date": date, "files": out}, nil
|
||||
}
|
||||
func (s *AuditService) LogContent(ctx context.Context, date, path string, cursor *int64) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().LogContent(ctx, date, path, cursor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"date": v.Date, "path": v.Path, "content": v.Content, "lineCount": v.LineCount, "nextCursor": v.NextCursor, "hasMore": v.HasMore, "limitedByBytes": v.LimitedByBytes, "size": v.Size, "modifiedAt": v.ModifiedAt}, nil
|
||||
}
|
||||
func errorDTO(v *biz.ErrorRecord) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "form": v.Form, "info": v.Info, "level": v.Level, "request_id": v.RequestID, "trace_id": v.TraceID, "solution": v.Solution, "status": v.Status}
|
||||
}
|
||||
func (s *AuditService) CreateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
return s.uc.Repo().CreateError(ctx, v)
|
||||
}
|
||||
func (s *AuditService) UpdateError(ctx context.Context, v *biz.ErrorRecord) error {
|
||||
return s.uc.Repo().UpdateError(ctx, v)
|
||||
}
|
||||
func (s *AuditService) DeleteErrors(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteErrors(ctx, ids)
|
||||
}
|
||||
func (s *AuditService) Error(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindError(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return errorDTO(v), nil
|
||||
}
|
||||
func (s *AuditService) Errors(ctx context.Context, page, size int, q *biz.ErrorRecord) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListErrors(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, errorDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"io"
|
||||
"kra/internal/biz"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type ExportService struct{ uc *biz.ExportUsecase }
|
||||
|
||||
func NewExportService(uc *biz.ExportUsecase) *ExportService { return &ExportService{uc: uc} }
|
||||
func exportDTO(v *biz.ExportTemplate) map[string]any {
|
||||
conditions := make([]map[string]any, 0, len(v.Conditions))
|
||||
for _, x := range v.Conditions {
|
||||
conditions = append(conditions, map[string]any{"from": x.From, "column": x.Column, "operator": x.Operator})
|
||||
}
|
||||
joins := make([]map[string]any, 0, len(v.Joins))
|
||||
for _, x := range v.Joins {
|
||||
joins = append(joins, map[string]any{"joins": x.Join, "table": x.Table, "on": x.On})
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "dbName": v.DBName, "name": v.Name, "tableName": v.TableName, "templateID": v.TemplateID, "templateInfo": v.TemplateInfo, "sql": v.SQL, "importSql": v.ImportSQL, "limit": v.Limit, "order": v.Order, "conditions": conditions, "joinTemplate": joins}
|
||||
}
|
||||
func (s *ExportService) Create(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
return s.uc.Repo().CreateExportTemplate(ctx, v)
|
||||
}
|
||||
func (s *ExportService) Update(ctx context.Context, v *biz.ExportTemplate) error {
|
||||
return s.uc.Repo().UpdateExportTemplate(ctx, v)
|
||||
}
|
||||
func (s *ExportService) Delete(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteExportTemplates(ctx, ids)
|
||||
}
|
||||
func (s *ExportService) Template(ctx context.Context, id uint, tid string) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindExportTemplate(ctx, id, tid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return exportDTO(v), nil
|
||||
}
|
||||
func (s *ExportService) Templates(ctx context.Context, page, size int, q *biz.ExportTemplate) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListExportTemplates(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, exportDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func templateColumns(raw string) ([]string, map[string]string, error) {
|
||||
labels := map[string]string{}
|
||||
if err := json.Unmarshal([]byte(raw), &labels); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
keys := make([]string, 0, len(labels))
|
||||
for key := range labels {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys, labels, nil
|
||||
}
|
||||
func (s *ExportService) Export(ctx context.Context, tid string, params map[string]string) ([]byte, string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
rows, _, err := s.uc.Repo().QueryExport(ctx, template, params)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
for rowIndex, row := range rows {
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, rowIndex+2)
|
||||
_ = file.SetCellValue(sheet, cell, row[key])
|
||||
}
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + ".xlsx", nil
|
||||
}
|
||||
func (s *ExportService) ExportBlankTemplate(ctx context.Context, tid string) ([]byte, string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := excelize.NewFile()
|
||||
sheet := file.GetSheetName(0)
|
||||
for column, key := range keys {
|
||||
cell, _ := excelize.CoordinatesToCellName(column+1, 1)
|
||||
_ = file.SetCellValue(sheet, cell, labels[key])
|
||||
}
|
||||
buffer, err := file.WriteToBuffer()
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return buffer.Bytes(), template.Name + "_template.xlsx", nil
|
||||
}
|
||||
func (s *ExportService) Preview(ctx context.Context, tid string, params map[string]string) (string, error) {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, sql, err := s.uc.Repo().QueryExport(ctx, template, params)
|
||||
return sql, err
|
||||
}
|
||||
func (s *ExportService) Import(ctx context.Context, tid string, reader io.Reader) error {
|
||||
template, err := s.uc.Repo().FindExportTemplate(ctx, 0, tid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := excelize.OpenReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
sheets := file.GetSheetList()
|
||||
if len(sheets) == 0 {
|
||||
return errors.New("工作簿没有工作表")
|
||||
}
|
||||
rows, err := file.GetRows(sheets[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rows) < 2 {
|
||||
return nil
|
||||
}
|
||||
keys, labels, err := templateColumns(template.TemplateInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reverse := map[string]string{}
|
||||
for _, key := range keys {
|
||||
reverse[labels[key]] = key
|
||||
}
|
||||
headers := rows[0]
|
||||
items := make([]map[string]any, 0, len(rows)-1)
|
||||
for rowIndex, row := range rows[1:] {
|
||||
item := map[string]any{}
|
||||
empty := true
|
||||
for column, value := range row {
|
||||
if column >= len(headers) {
|
||||
continue
|
||||
}
|
||||
key := reverse[headers[column]]
|
||||
if key != "" {
|
||||
item[key] = value
|
||||
if value != "" {
|
||||
empty = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if !empty {
|
||||
items = append(items, item)
|
||||
}
|
||||
_ = rowIndex
|
||||
}
|
||||
if err = s.uc.Repo().ImportExportRows(ctx, template, items); err != nil {
|
||||
return fmt.Errorf("导入失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
type MediaService struct{ uc *biz.MediaUsecase }
|
||||
|
||||
func NewMediaService(uc *biz.MediaUsecase) *MediaService { return &MediaService{uc: uc} }
|
||||
func mediaDTO(v *biz.MediaFile) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "classId": v.CategoryID, "url": v.URL, "tag": v.Tag, "key": v.Key, "size": v.Size, "mime": v.Mime, "md5": v.MD5, "userId": v.UserID}
|
||||
}
|
||||
func categoryDTO(v *biz.AttachmentCategory) map[string]any {
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, x := range v.Children {
|
||||
children = append(children, categoryDTO(x))
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "pid": v.ParentID, "children": children}
|
||||
}
|
||||
func (s *MediaService) Upload(ctx context.Context, userID uint, name, mime string, categoryID int, reader io.Reader, save bool) (map[string]any, error) {
|
||||
v, err := s.uc.Upload(ctx, userID, name, mime, categoryID, reader, save)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaDTO(v), nil
|
||||
}
|
||||
func (s *MediaService) Media(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindMedia(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaDTO(v), nil
|
||||
}
|
||||
func (s *MediaService) MediaList(ctx context.Context, page, size int, keyword string, categoryID int, tag string, userID uint) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListMedia(ctx, page, size, keyword, categoryID, tag, userID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, mediaDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *MediaService) Delete(ctx context.Context, id uint) error { return s.uc.Delete(ctx, id) }
|
||||
func (s *MediaService) Rename(ctx context.Context, id uint, name string) error {
|
||||
return s.uc.Repo().UpdateMediaName(ctx, id, name)
|
||||
}
|
||||
func (s *MediaService) ImportURLs(ctx context.Context, items []*biz.MediaFile) error {
|
||||
return s.uc.Repo().CreateMediaBatch(ctx, items)
|
||||
}
|
||||
func (s *MediaService) Storage(ctx context.Context, prefix, cursor string, limit int) ([]map[string]any, string, bool, error) {
|
||||
items, next, more, err := s.uc.ListStorage(ctx, prefix, cursor, limit)
|
||||
if err != nil {
|
||||
return nil, "", false, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"name": v.Name, "key": v.Path, "url": v.URL, "size": v.Size})
|
||||
}
|
||||
return out, next, more, nil
|
||||
}
|
||||
func (s *MediaService) Categories(ctx context.Context) ([]map[string]any, error) {
|
||||
items, err := s.uc.Repo().ListCategories(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, categoryDTO(v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *MediaService) SaveCategory(ctx context.Context, v *biz.AttachmentCategory) error {
|
||||
return s.uc.Repo().SaveCategory(ctx, v)
|
||||
}
|
||||
func (s *MediaService) DeleteCategory(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteCategory(ctx, id)
|
||||
}
|
||||
func (s *MediaService) InitUpload(ctx context.Context, userID uint, name, hash string, size, chunkSize int64, total int) (map[string]any, error) {
|
||||
session, media, chunks, err := s.uc.InitUpload(ctx, userID, name, hash, size, chunkSize, total)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if media != nil {
|
||||
return map[string]any{"instant": true, "media": mediaDTO(media), "uploadedChunks": []int{}}, nil
|
||||
}
|
||||
return map[string]any{"instant": false, "uploadId": session.ID, "uploadedChunks": chunks, "media": nil}, nil
|
||||
}
|
||||
func (s *MediaService) SaveChunk(ctx context.Context, userID, uploadID uint, index int, hash string, reader io.Reader) error {
|
||||
return s.uc.SaveChunk(ctx, userID, uploadID, index, hash, reader)
|
||||
}
|
||||
func (s *MediaService) CompleteUpload(ctx context.Context, userID, uploadID uint) (map[string]any, error) {
|
||||
v, err := s.uc.CompleteUpload(ctx, userID, uploadID, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaDTO(v), nil
|
||||
}
|
||||
func (s *MediaService) CancelUpload(ctx context.Context, userID, uploadID uint) error {
|
||||
return s.uc.CancelUpload(ctx, userID, uploadID)
|
||||
}
|
||||
@@ -3,4 +3,4 @@ package service
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSet is service providers.
|
||||
var ProviderSet = wire.NewSet(NewAdminService)
|
||||
var ProviderSet = wire.NewSet(NewAdminService, NewSystemService, NewAccessService, NewSettingsService, NewVersionService, NewExportService, NewAuditService, NewTaskService, NewMediaService)
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/pkg/adminauth"
|
||||
)
|
||||
|
||||
type SettingsService struct {
|
||||
uc *biz.SettingsUsecase
|
||||
config *conf.AdminBackend
|
||||
securityMu sync.RWMutex
|
||||
securityCache *biz.SecurityConfig
|
||||
}
|
||||
|
||||
func NewSettingsService(uc *biz.SettingsUsecase, config *conf.AdminBackend) *SettingsService {
|
||||
return &SettingsService{uc: uc, config: config}
|
||||
}
|
||||
func (s *SettingsService) Repo() biz.SettingsRepo { return s.uc.Repo() }
|
||||
|
||||
func dictionaryDTO(v *biz.Dictionary) map[string]any {
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, x := range v.Children {
|
||||
children = append(children, dictionaryDTO(x))
|
||||
}
|
||||
details := make([]map[string]any, 0, len(v.Details))
|
||||
for _, x := range v.Details {
|
||||
details = append(details, detailDTO(x))
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "type": v.Type, "status": v.Status, "desc": v.Desc, "parentID": v.ParentID, "children": children, "sysDictionaryDetails": details}
|
||||
}
|
||||
func detailDTO(v *biz.DictionaryDetail) map[string]any {
|
||||
children := make([]map[string]any, 0, len(v.Children))
|
||||
for _, x := range v.Children {
|
||||
children = append(children, detailDTO(x))
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "label": v.Label, "value": v.Value, "extend": v.Extend, "status": v.Status, "sort": v.Sort, "sysDictionaryID": v.DictionaryID, "parentID": v.ParentID, "level": v.Level, "path": v.Path, "disabled": !v.Status, "children": children}
|
||||
}
|
||||
func parameterDTO(v *biz.SystemParameter) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "key": v.Key, "value": v.Value, "desc": v.Desc}
|
||||
}
|
||||
func tokenDTO(v *biz.APIToken) map[string]any {
|
||||
var user any = nil
|
||||
if v.User != nil {
|
||||
user = convertUser(v.User)
|
||||
}
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "userId": v.UserID, "authorityId": v.AuthorityID, "token": v.Token, "status": v.Status, "expiresAt": v.ExpiresAt, "remark": v.Remark, "user": user}
|
||||
}
|
||||
|
||||
func (s *SettingsService) Dictionaries(ctx context.Context, page, size int, name, typ string, details bool) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListDictionaries(ctx, page, size, name, typ, details)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, dictionaryDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) Dictionary(ctx context.Context, id uint, typ string, details bool) (map[string]any, error) {
|
||||
v, err := s.Repo().FindDictionary(ctx, id, typ, details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dictionaryDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) CreateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
return s.Repo().CreateDictionary(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionary(ctx context.Context, v *biz.Dictionary) error {
|
||||
return s.Repo().UpdateDictionary(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteDictionary(ctx context.Context, id uint) error {
|
||||
return s.Repo().DeleteDictionary(ctx, id)
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetails(ctx context.Context, page, size int, dictionaryID uint, label, value string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListDictionaryDetails(ctx, page, size, dictionaryID, label, value)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, detailDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) DictionaryDetail(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.Repo().FindDictionaryDetail(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return detailDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) DictionaryTree(ctx context.Context, id uint, typ string) ([]map[string]any, error) {
|
||||
items, err := s.Repo().DictionaryDetailTree(ctx, id, typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, detailDTO(v))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (s *SettingsService) CreateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
return s.Repo().CreateDictionaryDetail(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateDictionaryDetail(ctx context.Context, v *biz.DictionaryDetail) error {
|
||||
return s.Repo().UpdateDictionaryDetail(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteDictionaryDetail(ctx context.Context, id uint) error {
|
||||
return s.Repo().DeleteDictionaryDetail(ctx, id)
|
||||
}
|
||||
|
||||
func (s *SettingsService) Parameters(ctx context.Context, page, size int, q *biz.SystemParameter) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListParameters(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, parameterDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) Parameter(ctx context.Context, id uint, key string) (map[string]any, error) {
|
||||
v, err := s.Repo().FindParameter(ctx, id, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parameterDTO(v), nil
|
||||
}
|
||||
func (s *SettingsService) CreateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
return s.Repo().CreateParameter(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) UpdateParameter(ctx context.Context, v *biz.SystemParameter) error {
|
||||
return s.Repo().UpdateParameter(ctx, v)
|
||||
}
|
||||
func (s *SettingsService) DeleteParameters(ctx context.Context, ids []uint) error {
|
||||
return s.Repo().DeleteParameters(ctx, ids)
|
||||
}
|
||||
|
||||
func (s *SettingsService) CreateAPIToken(ctx context.Context, userID, authorityID uint, days int, remark string) (string, error) {
|
||||
user, allowed, err := s.Repo().UserHasAuthority(ctx, userID, authorityID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !allowed {
|
||||
return "", errors.New("用户不具备该角色权限")
|
||||
}
|
||||
duration := time.Duration(days) * 24 * time.Hour
|
||||
if days == -1 {
|
||||
duration = 100 * 365 * 24 * time.Hour
|
||||
}
|
||||
if duration <= 0 {
|
||||
return "", errors.New("有效天数必须大于0或为-1")
|
||||
}
|
||||
secret, issuer := "", "kra"
|
||||
if s.config != nil && s.config.Jwt != nil {
|
||||
secret = s.config.Jwt.SigningKey
|
||||
if s.config.Jwt.Issuer != "" {
|
||||
issuer = s.config.Jwt.Issuer
|
||||
}
|
||||
}
|
||||
token, _, err := adminauth.Generate(secret, issuer, duration, user.ID, authorityID, user.UUID, user.Username, user.NickName, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
record := &biz.APIToken{UserID: userID, AuthorityID: authorityID, Token: token, Status: true, ExpiresAt: time.Now().Add(duration), Remark: remark}
|
||||
if err = s.Repo().CreateAPIToken(ctx, record); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
func (s *SettingsService) APITokens(ctx context.Context, page, size int, userID uint, status *bool) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.Repo().ListAPITokens(ctx, page, size, userID, status)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, tokenDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *SettingsService) DisableAPIToken(ctx context.Context, id uint) error {
|
||||
_, err := s.Repo().DisableAPIToken(ctx, id)
|
||||
return err
|
||||
}
|
||||
func (s *SettingsService) IsTokenDisabled(ctx context.Context, token string) (bool, error) {
|
||||
return s.Repo().IsTokenDisabled(ctx, token)
|
||||
}
|
||||
|
||||
func securityDTO(v *biz.SecurityConfig) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "captchaOpen": v.CaptchaOpen, "captchaTimeout": v.CaptchaTimeout, "keyLong": v.KeyLong, "imgWidth": v.ImgWidth, "imgHeight": v.ImgHeight, "pwdMinLength": v.PwdMinLength, "pwdRequireUpper": v.PwdRequireUpper, "pwdRequireLower": v.PwdRequireLower, "pwdRequireDigit": v.PwdRequireDigit, "pwdRequireSpecial": v.PwdRequireSpecial, "limitEnable": v.LimitEnable, "limitWindow": v.LimitWindow, "limitCount": v.LimitCount, "lockEnable": v.LockEnable, "lockThreshold": v.LockThreshold, "lockDuration": v.LockDuration, "pwdExpireEnable": v.PwdExpireEnable, "pwdExpireDays": v.PwdExpireDays, "forceNewUserChangePassword": v.ForceNewUserChangePassword}
|
||||
}
|
||||
func (s *SettingsService) CurrentSecurity(ctx context.Context) (*biz.SecurityConfig, error) {
|
||||
s.securityMu.RLock()
|
||||
if s.securityCache != nil {
|
||||
copy := *s.securityCache
|
||||
s.securityMu.RUnlock()
|
||||
return ©, nil
|
||||
}
|
||||
s.securityMu.RUnlock()
|
||||
value, err := s.Repo().SecurityConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.securityMu.Lock()
|
||||
s.securityCache = value
|
||||
copy := *value
|
||||
s.securityMu.Unlock()
|
||||
return ©, nil
|
||||
}
|
||||
func (s *SettingsService) Security(ctx context.Context) (map[string]any, error) {
|
||||
value, err := s.CurrentSecurity(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return securityDTO(value), nil
|
||||
}
|
||||
func (s *SettingsService) SaveSecurity(ctx context.Context, value *biz.SecurityConfig) (map[string]any, error) {
|
||||
if value.KeyLong < 1 || value.ImgWidth < 1 || value.ImgHeight < 1 || value.PwdMinLength < 1 || value.LimitWindow < 1 || value.LimitCount < 1 || value.LockThreshold < 1 || value.LockDuration < 1 || value.PwdExpireDays < 1 {
|
||||
return nil, errors.New("安全配置数值必须大于0")
|
||||
}
|
||||
if err := s.Repo().SaveSecurityConfig(ctx, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.securityMu.Lock()
|
||||
s.securityCache = value
|
||||
s.securityMu.Unlock()
|
||||
return securityDTO(value), nil
|
||||
}
|
||||
func (s *SettingsService) ValidatePassword(ctx context.Context, password string) error {
|
||||
cfg, err := s.CurrentSecurity(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len([]rune(password)) < cfg.PwdMinLength {
|
||||
return errors.New("密码长度不足")
|
||||
}
|
||||
hasUpper, hasLower, hasDigit, hasSpecial := false, false, false, false
|
||||
for _, ch := range password {
|
||||
switch {
|
||||
case ch >= 'A' && ch <= 'Z':
|
||||
hasUpper = true
|
||||
case ch >= 'a' && ch <= 'z':
|
||||
hasLower = true
|
||||
case ch >= '0' && ch <= '9':
|
||||
hasDigit = true
|
||||
default:
|
||||
hasSpecial = true
|
||||
}
|
||||
}
|
||||
if cfg.PwdRequireUpper && !hasUpper {
|
||||
return errors.New("密码必须包含大写字母")
|
||||
}
|
||||
if cfg.PwdRequireLower && !hasLower {
|
||||
return errors.New("密码必须包含小写字母")
|
||||
}
|
||||
if cfg.PwdRequireDigit && !hasDigit {
|
||||
return errors.New("密码必须包含数字")
|
||||
}
|
||||
if cfg.PwdRequireSpecial && !hasSpecial {
|
||||
return errors.New("密码必须包含特殊字符")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
"kra/internal/conf"
|
||||
"kra/pkg/adminauth"
|
||||
)
|
||||
|
||||
type SystemService struct {
|
||||
uc *biz.SystemUsecase
|
||||
config *conf.AdminBackend
|
||||
settings *SettingsService
|
||||
}
|
||||
|
||||
type LoginResult struct {
|
||||
User map[string]any `json:"user"`
|
||||
Token string `json:"token"`
|
||||
ExpiresAt int64 `json:"expiresAt"`
|
||||
NeedChangePassword bool `json:"needChangePassword"`
|
||||
}
|
||||
|
||||
func NewSystemService(uc *biz.SystemUsecase, config *conf.AdminBackend, settings *SettingsService) *SystemService {
|
||||
return &SystemService{uc: uc, config: config, settings: settings}
|
||||
}
|
||||
|
||||
func (s *SystemService) IsInitialized(ctx context.Context) (bool, error) {
|
||||
return s.uc.IsInitialized(ctx)
|
||||
}
|
||||
func (s *SystemService) Initialize(ctx context.Context) error { return s.uc.Initialize(ctx) }
|
||||
|
||||
func (s *SystemService) CacheGet(ctx context.Context, key string) (string, bool, error) {
|
||||
return s.uc.CacheGet(ctx, key)
|
||||
}
|
||||
func (s *SystemService) CacheSet(ctx context.Context, key, value string, expiration time.Duration) error {
|
||||
return s.uc.CacheSet(ctx, key, value, expiration)
|
||||
}
|
||||
func (s *SystemService) CacheDelete(ctx context.Context, key string) error {
|
||||
return s.uc.CacheDelete(ctx, key)
|
||||
}
|
||||
|
||||
func (s *SystemService) Login(ctx context.Context, username, password string) (*LoginResult, error) {
|
||||
u, err := s.uc.Login(ctx, username, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.Enable != 1 {
|
||||
return nil, biz.ErrInvalidCredentials
|
||||
}
|
||||
if security, securityErr := s.settings.CurrentSecurity(ctx); securityErr == nil && security.PwdExpireEnable && u.PasswordUpdatedAt != nil && time.Since(*u.PasswordUpdatedAt) > time.Duration(security.PwdExpireDays)*24*time.Hour {
|
||||
u.MustChangePassword = true
|
||||
}
|
||||
expires := 7 * 24 * time.Hour
|
||||
secret, issuer := "", "kra"
|
||||
if s.config != nil && s.config.Jwt != nil {
|
||||
secret, issuer = s.config.Jwt.SigningKey, s.config.Jwt.Issuer
|
||||
if s.config.Jwt.ExpiresTime != nil {
|
||||
expires = s.config.Jwt.ExpiresTime.AsDuration()
|
||||
}
|
||||
}
|
||||
token, claims, err := adminauth.Generate(secret, issuer, expires, u.ID, u.AuthorityID, u.UUID, u.Username, u.NickName, u.MustChangePassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LoginResult{User: convertUser(u), Token: token, ExpiresAt: claims.ExpiresAt.UnixMilli(), NeedChangePassword: u.MustChangePassword}, nil
|
||||
}
|
||||
|
||||
func (s *SystemService) User(ctx context.Context, id uint) (map[string]any, error) {
|
||||
u, err := s.uc.User(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertUser(u), nil
|
||||
}
|
||||
|
||||
func (s *SystemService) Menus(ctx context.Context, authorityID uint) ([]map[string]any, error) {
|
||||
menus, err := s.uc.Menus(ctx, authorityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]map[string]any, 0, len(menus))
|
||||
for _, menu := range menus {
|
||||
result = append(result, convertMenu(menu))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type UserInput struct {
|
||||
ID uint
|
||||
Username, Password, NickName, HeaderImg, Phone, Email string
|
||||
AuthorityID uint
|
||||
Enable int
|
||||
AuthorityIDs []uint
|
||||
}
|
||||
|
||||
func (s *SystemService) ListUsers(ctx context.Context, page, pageSize int, filter *biz.UserListFilter) ([]map[string]any, int64, error) {
|
||||
users, total, err := s.uc.ListUsers(ctx, page, pageSize, filter)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
result := make([]map[string]any, 0, len(users))
|
||||
for _, u := range users {
|
||||
result = append(result, convertUser(u))
|
||||
}
|
||||
return result, total, nil
|
||||
}
|
||||
func (s *SystemService) CreateUser(ctx context.Context, in UserInput) (map[string]any, error) {
|
||||
if err := s.settings.ValidatePassword(ctx, in.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
security, _ := s.settings.CurrentSecurity(ctx)
|
||||
mustChange := security != nil && security.ForceNewUserChangePassword
|
||||
if in.AuthorityID == 0 && len(in.AuthorityIDs) > 0 {
|
||||
in.AuthorityID = in.AuthorityIDs[0]
|
||||
}
|
||||
in.AuthorityIDs = includeAuthority(in.AuthorityIDs, in.AuthorityID)
|
||||
u, err := s.uc.CreateUser(ctx, &biz.User{Username: in.Username, Password: in.Password, NickName: in.NickName, HeaderImg: in.HeaderImg, AuthorityID: in.AuthorityID, Phone: in.Phone, Email: in.Email, Enable: in.Enable, MustChangePassword: mustChange})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(in.AuthorityIDs) > 0 {
|
||||
if err = s.uc.SetUserAuthorities(ctx, u.ID, in.AuthorityIDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err = s.uc.User(ctx, u.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return convertUser(u), nil
|
||||
}
|
||||
func (s *SystemService) UpdateUser(ctx context.Context, in UserInput) error {
|
||||
in.AuthorityIDs = includeAuthority(in.AuthorityIDs, in.AuthorityID)
|
||||
if err := s.uc.UpdateUser(ctx, &biz.User{ID: in.ID, NickName: in.NickName, HeaderImg: in.HeaderImg, AuthorityID: in.AuthorityID, Phone: in.Phone, Email: in.Email, Enable: in.Enable}); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(in.AuthorityIDs) > 0 {
|
||||
return s.uc.SetUserAuthorities(ctx, in.ID, in.AuthorityIDs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func includeAuthority(ids []uint, primary uint) []uint {
|
||||
if primary == 0 {
|
||||
return ids
|
||||
}
|
||||
for _, id := range ids {
|
||||
if id == primary {
|
||||
return ids
|
||||
}
|
||||
}
|
||||
return append(ids, primary)
|
||||
}
|
||||
func (s *SystemService) DeleteUser(ctx context.Context, id uint) error {
|
||||
return s.uc.DeleteUser(ctx, id)
|
||||
}
|
||||
func (s *SystemService) ResetPassword(ctx context.Context, id uint, password string) error {
|
||||
if err := s.settings.ValidatePassword(ctx, password); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.ResetPassword(ctx, id, password)
|
||||
}
|
||||
func (s *SystemService) ChangePassword(ctx context.Context, id uint, oldPassword, newPassword string) error {
|
||||
if err := s.settings.ValidatePassword(ctx, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.ChangePassword(ctx, id, oldPassword, newPassword)
|
||||
}
|
||||
func (s *SystemService) Authorities(ctx context.Context) ([]map[string]any, error) {
|
||||
authorities, err := s.uc.Authorities(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]map[string]any, 0, len(authorities))
|
||||
for _, a := range authorities {
|
||||
result = append(result, convertAuthority(*a))
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
func (s *SystemService) SetUserAuthorities(ctx context.Context, id uint, authorityIDs []uint) error {
|
||||
return s.uc.SetUserAuthorities(ctx, id, authorityIDs)
|
||||
}
|
||||
func (s *SystemService) SwitchAuthority(ctx context.Context, id, authorityID uint) (*LoginResult, error) {
|
||||
if err := s.uc.SetUserAuthority(ctx, id, authorityID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err := s.uc.User(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expires := 7 * 24 * time.Hour
|
||||
secret, issuer := "", "kra"
|
||||
if s.config != nil && s.config.Jwt != nil {
|
||||
secret, issuer = s.config.Jwt.SigningKey, s.config.Jwt.Issuer
|
||||
if s.config.Jwt.ExpiresTime != nil {
|
||||
expires = s.config.Jwt.ExpiresTime.AsDuration()
|
||||
}
|
||||
}
|
||||
token, claims, err := adminauth.Generate(secret, issuer, expires, u.ID, authorityID, u.UUID, u.Username, u.NickName, u.MustChangePassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LoginResult{User: convertUser(u), Token: token, ExpiresAt: claims.ExpiresAt.UnixMilli(), NeedChangePassword: u.MustChangePassword}, nil
|
||||
}
|
||||
func (s *SystemService) SetUserSetting(ctx context.Context, id uint, setting map[string]any) error {
|
||||
return s.uc.SetUserSetting(ctx, id, setting)
|
||||
}
|
||||
|
||||
func convertAuthority(a biz.Authority) map[string]any {
|
||||
return map[string]any{"authorityId": a.AuthorityID, "authorityName": a.AuthorityName, "parentId": a.ParentID, "children": []map[string]any{}, "menus": []any{}, "dataScope": a.DataScope, "defaultRouter": a.DefaultRouter}
|
||||
}
|
||||
|
||||
func convertUser(u *biz.User) map[string]any {
|
||||
authorities := make([]map[string]any, 0, len(u.Authorities))
|
||||
for _, a := range u.Authorities {
|
||||
authorities = append(authorities, convertAuthority(a))
|
||||
}
|
||||
departments := make([]map[string]any, 0, len(u.Departments))
|
||||
for i := range u.Departments {
|
||||
departments = append(departments, departmentDTO(&u.Departments[i]))
|
||||
}
|
||||
positions := make([]map[string]any, 0, len(u.Positions))
|
||||
for i := range u.Positions {
|
||||
positions = append(positions, positionDTO(&u.Positions[i]))
|
||||
}
|
||||
dept := map[string]any{}
|
||||
if u.Department != nil {
|
||||
dept = departmentDTO(u.Department)
|
||||
}
|
||||
return map[string]any{"ID": u.ID, "CreatedAt": u.CreatedAt, "UpdatedAt": u.UpdatedAt, "DeletedAt": nil, "uuid": u.UUID, "userName": u.Username, "nickName": u.NickName, "headerImg": u.HeaderImg, "authorityId": u.AuthorityID, "authority": convertAuthority(u.Authority), "authorities": authorities, "deptId": u.DeptID, "dept": dept, "departments": departments, "positions": positions, "phone": u.Phone, "email": u.Email, "enable": u.Enable, "originSetting": u.OriginSetting}
|
||||
}
|
||||
|
||||
func convertMenu(menu *biz.Menu) map[string]any {
|
||||
children := make([]map[string]any, 0, len(menu.Children))
|
||||
for _, child := range menu.Children {
|
||||
children = append(children, convertMenu(child))
|
||||
}
|
||||
buttons := make([]map[string]any, 0, len(menu.Buttons))
|
||||
for _, button := range menu.Buttons {
|
||||
buttons = append(buttons, map[string]any{"ID": button.ID, "name": button.Name, "desc": button.Description, "sysBaseMenuID": button.MenuID})
|
||||
}
|
||||
return map[string]any{"ID": menu.ID, "parentId": menu.ParentID, "path": menu.Path, "name": menu.Name, "hidden": menu.Hidden, "component": menu.Component, "sort": menu.Sort, "meta": map[string]any{"activeName": menu.ActiveName, "keepAlive": menu.KeepAlive, "defaultMenu": menu.DefaultMenu, "title": menu.Title, "icon": menu.Icon, "closeTab": menu.CloseTab, "transitionType": menu.TransitionType}, "children": children, "parameters": []any{}, "menuBtn": buttons}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/robfig/cron/v3"
|
||||
"io"
|
||||
"kra/internal/biz"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TaskService struct{ uc *biz.TaskUsecase }
|
||||
|
||||
func NewTaskService(uc *biz.TaskUsecase) *TaskService { return &TaskService{uc: uc} }
|
||||
func (s *TaskService) Validate(v *biz.TimedTask) error {
|
||||
if v.Name == "" {
|
||||
return errors.New("任务名不能为空")
|
||||
}
|
||||
var err error
|
||||
if v.WithSeconds {
|
||||
_, err = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor).Parse(v.Spec)
|
||||
} else {
|
||||
_, err = cron.ParseStandard(v.Spec)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("cron 表达式非法: %w", err)
|
||||
}
|
||||
switch v.ExecutorType {
|
||||
case "method":
|
||||
if v.MethodName != "ClearDB" {
|
||||
return errors.New("方法未注册")
|
||||
}
|
||||
if len(v.Params) > 0 && !json.Valid(v.Params) {
|
||||
return errors.New("params 必须是合法 JSON")
|
||||
}
|
||||
case "http":
|
||||
parsed, parseErr := url.Parse(v.HTTPURL)
|
||||
if parseErr != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Hostname() == "" || parsed.User != nil {
|
||||
return errors.New("httpUrl 必须是合法的 http/https 地址")
|
||||
}
|
||||
if len(v.HTTPHeader) > 0 {
|
||||
headers := map[string]string{}
|
||||
if json.Unmarshal(v.HTTPHeader, &headers) != nil {
|
||||
return errors.New("httpHeader 必须是 JSON 对象")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return errors.New("executorType 必须为 method 或 http")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *TaskService) Create(ctx context.Context, v *biz.TimedTask) error {
|
||||
if err := s.Validate(v); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.Repo().CreateTask(ctx, v)
|
||||
}
|
||||
func (s *TaskService) Update(ctx context.Context, v *biz.TimedTask) error {
|
||||
if v.ID == 0 {
|
||||
return errors.New("缺少任务 ID")
|
||||
}
|
||||
if err := s.Validate(v); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.Repo().UpdateTask(ctx, v)
|
||||
}
|
||||
func (s *TaskService) Delete(ctx context.Context, id uint) error {
|
||||
return s.uc.Repo().DeleteTask(ctx, id)
|
||||
}
|
||||
func (s *TaskService) Toggle(ctx context.Context, id uint, enabled bool) error {
|
||||
return s.uc.Repo().ToggleTask(ctx, id, enabled)
|
||||
}
|
||||
func (s *TaskService) Task(ctx context.Context, id uint) (*biz.TimedTask, error) {
|
||||
return s.uc.Repo().FindTask(ctx, id)
|
||||
}
|
||||
func taskDTO(v *biz.TimedTask, next *time.Time) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "name": v.Name, "description": v.Description, "spec": v.Spec, "withSeconds": v.WithSeconds, "executorType": v.ExecutorType, "methodName": v.MethodName, "params": json.RawMessage(v.Params), "httpUrl": v.HTTPURL, "httpMethod": v.HTTPMethod, "httpHeader": json.RawMessage(v.HTTPHeader), "httpBody": v.HTTPBody, "httpAllowPrivate": v.HTTPAllowPrivate, "enabled": v.Enabled, "nextRunAt": next}
|
||||
}
|
||||
func (s *TaskService) Tasks(ctx context.Context, page, size int, q *biz.TimedTask, next map[uint]time.Time) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListTasks(ctx, page, size, q)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
var ptr *time.Time
|
||||
if value, ok := next[v.ID]; ok {
|
||||
copy := value
|
||||
ptr = ©
|
||||
}
|
||||
out = append(out, taskDTO(v, ptr))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *TaskService) Logs(ctx context.Context, page, size int, taskID uint, status string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListTaskLogs(ctx, page, size, taskID, status)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "DeletedAt": nil, "taskId": v.TaskID, "taskName": v.TaskName, "triggerType": v.TriggerType, "startedAt": v.StartedAt, "finishedAt": v.FinishedAt, "durationMs": v.DurationMS, "status": v.Status, "errorMsg": v.ErrorMsg, "output": v.Output})
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func privateIP(ip net.IP) bool {
|
||||
return ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()
|
||||
}
|
||||
func (s *TaskService) runHTTP(ctx context.Context, v *biz.TimedTask) (string, error) {
|
||||
parsed, _ := url.Parse(v.HTTPURL)
|
||||
if !v.HTTPAllowPrivate {
|
||||
ips, err := net.DefaultResolver.LookupIP(ctx, "ip", parsed.Hostname())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, ip := range ips {
|
||||
if privateIP(ip) {
|
||||
return "", errors.New("禁止访问内网或环回地址")
|
||||
}
|
||||
}
|
||||
}
|
||||
method := strings.ToUpper(v.HTTPMethod)
|
||||
if method == "" {
|
||||
method = http.MethodGet
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, method, v.HTTPURL, bytes.NewBufferString(v.HTTPBody))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
headers := map[string]string{}
|
||||
_ = json.Unmarshal(v.HTTPHeader, &headers)
|
||||
for key, value := range headers {
|
||||
request.Header.Set(key, value)
|
||||
}
|
||||
response, err := (&http.Client{Timeout: 30 * time.Second}).Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, _ := io.ReadAll(io.LimitReader(response.Body, 4096))
|
||||
output := fmt.Sprintf("HTTP %d: %s", response.StatusCode, string(body))
|
||||
if response.StatusCode < 200 || response.StatusCode >= 300 {
|
||||
return output, fmt.Errorf("HTTP 状态码 %d", response.StatusCode)
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
func (s *TaskService) Run(ctx context.Context, v *biz.TimedTask, trigger string) (log *biz.TimedTaskLog) {
|
||||
started := time.Now()
|
||||
log = &biz.TimedTaskLog{TaskID: v.ID, TaskName: v.Name, TriggerType: trigger, StartedAt: started, Status: "success"}
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
log.Status = "fail"
|
||||
log.ErrorMsg = fmt.Sprint(recovered)
|
||||
}
|
||||
log.FinishedAt = time.Now()
|
||||
log.DurationMS = log.FinishedAt.Sub(started).Milliseconds()
|
||||
_ = s.uc.Repo().RecordTaskLog(context.Background(), log)
|
||||
}()
|
||||
runCtx, cancel := context.WithTimeout(ctx, 35*time.Second)
|
||||
defer cancel()
|
||||
var err error
|
||||
switch v.ExecutorType {
|
||||
case "method":
|
||||
if v.MethodName == "ClearDB" {
|
||||
days := 30
|
||||
var params struct {
|
||||
Days int `json:"days"`
|
||||
}
|
||||
_ = json.Unmarshal(v.Params, ¶ms)
|
||||
if params.Days > 0 {
|
||||
days = params.Days
|
||||
}
|
||||
err = s.uc.Repo().CleanupLogs(runCtx, time.Now().AddDate(0, 0, -days))
|
||||
log.Output = "过期日志清理完成"
|
||||
}
|
||||
case "http":
|
||||
log.Output, err = s.runHTTP(runCtx, v)
|
||||
}
|
||||
if err != nil {
|
||||
log.Status = "fail"
|
||||
log.ErrorMsg = err.Error()
|
||||
}
|
||||
return log
|
||||
}
|
||||
func (s *TaskService) RegisteredMethods() []map[string]any {
|
||||
return []map[string]any{{"name": "ClearDB", "description": "清理数据库过期日志(操作记录、登录日志、定时任务日志)"}}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"kra/internal/biz"
|
||||
)
|
||||
|
||||
type VersionService struct{ uc *biz.VersionUsecase }
|
||||
|
||||
func NewVersionService(uc *biz.VersionUsecase) *VersionService { return &VersionService{uc: uc} }
|
||||
func versionDTO(v *biz.Version) map[string]any {
|
||||
return map[string]any{"ID": v.ID, "CreatedAt": v.CreatedAt, "UpdatedAt": v.UpdatedAt, "DeletedAt": nil, "versionName": v.Name, "versionCode": v.Code, "description": v.Description, "versionData": v.Data}
|
||||
}
|
||||
func (s *VersionService) Versions(ctx context.Context, page, size int, name, code string) ([]map[string]any, int64, error) {
|
||||
items, total, err := s.uc.Repo().ListVersions(ctx, page, size, name, code)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
out := make([]map[string]any, 0, len(items))
|
||||
for _, v := range items {
|
||||
out = append(out, versionDTO(v))
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (s *VersionService) Version(ctx context.Context, id uint) (map[string]any, error) {
|
||||
v, err := s.uc.Repo().FindVersion(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return versionDTO(v), nil
|
||||
}
|
||||
func (s *VersionService) VersionData(ctx context.Context, id uint) (string, string, error) {
|
||||
v, err := s.uc.Repo().FindVersion(ctx, id)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return v.Data, v.Code, nil
|
||||
}
|
||||
func (s *VersionService) DeleteVersions(ctx context.Context, ids []uint) error {
|
||||
return s.uc.Repo().DeleteVersions(ctx, ids)
|
||||
}
|
||||
func (s *VersionService) Export(ctx context.Context, name, code, description string, menuIDs, apiIDs, dictIDs []uint) error {
|
||||
bundle, err := s.uc.Repo().BuildVersionBundle(ctx, menuIDs, apiIDs, dictIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bundle.Name = name
|
||||
bundle.Code = code
|
||||
bundle.Description = description
|
||||
bundle.ExportTime = time.Now().Format("2006-01-02 15:04:05")
|
||||
menus := make([]map[string]any, 0, len(bundle.Menus))
|
||||
for _, v := range bundle.Menus {
|
||||
menus = append(menus, convertMenu(v))
|
||||
}
|
||||
apis := make([]map[string]any, 0, len(bundle.APIs))
|
||||
for _, v := range bundle.APIs {
|
||||
apis = append(apis, apiDTO(v))
|
||||
}
|
||||
dicts := make([]map[string]any, 0, len(bundle.Dictionaries))
|
||||
for _, v := range bundle.Dictionaries {
|
||||
dicts = append(dicts, dictionaryDTO(v))
|
||||
}
|
||||
payload := map[string]any{"version": map[string]any{"name": name, "code": code, "description": description, "exportTime": bundle.ExportTime}, "menus": menus, "apis": apis, "dictionaries": dicts}
|
||||
raw, err := json.MarshalIndent(payload, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.uc.Repo().CreateVersion(ctx, &biz.Version{Name: name, Code: code, Description: description, Data: string(raw)})
|
||||
}
|
||||
func (s *VersionService) Import(ctx context.Context, bundle *biz.VersionBundle) error {
|
||||
return s.uc.Repo().ImportVersionBundle(ctx, bundle)
|
||||
}
|
||||
Reference in New Issue
Block a user