feat: 实训教学模块

This commit is contained in:
TsMask
2024-11-23 10:44:22 +08:00
parent e5212c47ba
commit 7543dce04f
20 changed files with 1339 additions and 1614 deletions

View File

@@ -1,42 +1,461 @@
package service
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"be.ems/src/framework/constants/uploadsubpath"
"be.ems/src/framework/utils/file"
"be.ems/src/framework/utils/parse"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
"be.ems/src/modules/practical_training/model"
"be.ems/src/modules/practical_training/repository"
"github.com/xuri/excelize/v2"
)
// IPtNeConfigDataService 服务层接口
type IPtNeConfigDataService interface {
// SelectPage 根据条件分页查询字典类型
SelectPage(query map[string]any) map[string]any
// SelectList 根据实体查询
SelectList(param model.PtNeConfigData) []model.PtNeConfigData
// SelectByIds 通过ID查询
SelectById(paramId string) model.PtNeConfigData
// Insert 新增信息
Insert(param model.PtNeConfigData) string
// Update 修改信息
Update(param model.PtNeConfigData) int64
// DeleteByIds 批量删除信息
DeleteByIds(paramIds []string) (int64, error)
// SaveAsDefault 系统网元配置保存为系统示例
SaveAsDefault(neType, neId string)
// ResetAsDefault 重置配置示例
// stubType2个人为班级示例 1班级为系统示例
ResetAsDefault(operaUserName, stubType, neType, deptId string)
// SelectByStubType 通过存根类型查询
SelectByStubType(param model.PtNeConfigData) model.PtNeConfigData
// ApplyToNe 参数应用到网元
ApplyToNe(paramUser, neType string) error
// ExportToExcel 导出网元的全部配置项数据
ExportToExcel(operaUserName, fileName string) (string, error)
// NewPtNeConfigDataService 实例化服务层
var NewPtNeConfigDataService = &PtNeConfigDataService{
ptNeConfigDataLogRepository: repository.NewPtNeConfigDataLogRepository,
ptNeConfigDataRepository: repository.NewPtNeConfigDataRepository,
neConfigService: neService.NewNeConfig,
neInfoService: neService.NewNeInfo,
}
// PtNeConfigDataService 服务层处理
type PtNeConfigDataService struct {
// 实训教学_网元参数配置数据变更日志
ptNeConfigDataLogRepository *repository.PtNeConfigDataLogRepository
// 实训教学_网元参数配置表
ptNeConfigDataRepository *repository.PtNeConfigDataRepository
// 网元参数配置可用属性值服务
neConfigService *neService.NeConfig
// 网元信息服务
neInfoService *neService.NeInfo
}
// SelectNeHostPage 分页查询列表数据
func (r *PtNeConfigDataService) SelectPage(query map[string]any) (int64, []model.PtNeConfigData) {
return r.ptNeConfigDataRepository.SelectPage(query)
}
// SelectConfigList 查询列表
func (r *PtNeConfigDataService) SelectList(param model.PtNeConfigData) []model.PtNeConfigData {
return r.ptNeConfigDataRepository.SelectList(param)
}
// SelectByIds 通过ID查询
func (r *PtNeConfigDataService) SelectById(paramId string) model.PtNeConfigData {
if paramId == "" {
return model.PtNeConfigData{}
}
neHosts := r.ptNeConfigDataRepository.SelectByIds([]string{paramId})
if len(neHosts) > 0 {
return neHosts[0]
}
return model.PtNeConfigData{}
}
// Insert 新增信息
func (r *PtNeConfigDataService) Insert(param model.PtNeConfigData) int64 {
return r.ptNeConfigDataRepository.Insert(param)
}
// Update 修改信息
func (r *PtNeConfigDataService) Update(param model.PtNeConfigData) int64 {
return r.ptNeConfigDataRepository.Update(param)
}
// DeleteByIds 批量删除信息
func (r *PtNeConfigDataService) DeleteByIds(paramIds []string) (int64, error) {
// 检查是否存在
ids := r.ptNeConfigDataRepository.SelectByIds(paramIds)
if len(ids) <= 0 {
return 0, fmt.Errorf("ptNeConfigData.noData")
}
if len(ids) == len(paramIds) {
rows := r.ptNeConfigDataRepository.DeleteByIds(paramIds)
return rows, nil
}
// 删除信息失败!
return 0, fmt.Errorf("delete fail")
}
// SaveAsDefault 系统网元配置保存为系统示例
func (r *PtNeConfigDataService) SaveAsDefault(neType, neId string) {
// 查询网元获取IP获取网元状态
neInfo := r.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId || neInfo.IP == "" {
return
}
stubType := "0"
operaUserName := "admin"
deptId := "100"
confs := r.neConfigService.SelectList(neModel.NeConfig{NeType: neInfo.NeType})
for _, v := range confs {
// 查询是否存在记录
hasItems := r.SelectList(model.PtNeConfigData{
NeType: v.NeType,
StubType: stubType,
ParamName: v.ParamName,
ParamType: v.ParamType,
})
// 网元直连 获取网元配置信息
resData, err := neFetchlink.NeConfigInfo(neInfo, v.ParamName)
if err != nil {
continue
}
// 将json数据转字符串存储 data:[{},{}]
data, ok := resData["data"]
if !ok {
continue
}
paramDataByte, err := json.Marshal(data)
if err != nil {
continue
}
// 插入
if len(hasItems) == 0 {
r.Insert(model.PtNeConfigData{
CreateBy: operaUserName,
StubType: stubType,
NeType: v.NeType,
ParamName: v.ParamName,
ParamDisplay: v.ParamDisplay,
ParamType: v.ParamType,
ParamJson: string(paramDataByte),
DeptId: deptId,
})
}
// 更新
if len(hasItems) == 1 {
item := hasItems[0]
item.UpdateBy = operaUserName
item.ParamDisplay = v.ParamDisplay
item.ParamJson = string(paramDataByte)
item.DeptId = deptId
r.Update(item)
}
}
}
// ResetAsDefault 重置配置示例
// stubType2个人为班级示例 1班级为系统示例
func (r *PtNeConfigDataService) ResetAsDefault(operaUserName, stubType, neType, deptId string) {
rootStubType := "1"
if stubType == "1" {
rootStubType = "0"
}
if stubType == "2" {
rootStubType = "1"
}
ptConfs := r.SelectList(model.PtNeConfigData{StubType: rootStubType, NeType: neType})
for _, v := range ptConfs {
// 查询是否存在记录
hasItems := r.SelectList(model.PtNeConfigData{
CreateBy: operaUserName,
NeType: v.NeType,
StubType: stubType,
ParamName: v.ParamName,
ParamType: v.ParamType,
})
// 插入
if len(hasItems) == 0 {
r.Insert(model.PtNeConfigData{
CreateBy: operaUserName,
StubType: stubType,
NeType: v.NeType,
ParamName: v.ParamName,
ParamDisplay: v.ParamDisplay,
ParamType: v.ParamType,
ParamJson: v.ParamJson,
DeptId: deptId,
})
// 保留变更日志
changeLog := model.PtNeConfigDataLog{
CreateBy: operaUserName,
StubType: stubType,
NeType: v.NeType,
ParamName: v.ParamName,
ParamDisplay: v.ParamDisplay,
ParamType: v.ParamType,
OperaType: 0,
ParamJsonOld: "[]",
ParamJsonNew: v.ParamJson,
}
r.ptNeConfigDataLogRepository.Insert(changeLog)
}
// 更新
if len(hasItems) == 1 {
item := hasItems[0]
item.UpdateBy = operaUserName
item.ParamDisplay = v.ParamDisplay
item.ParamJson = v.ParamJson
item.DeptId = deptId
r.Update(item)
// 保留变更日志
changeLog := model.PtNeConfigDataLog{
CreateBy: operaUserName,
StubType: hasItems[0].StubType,
NeType: v.NeType,
ParamName: v.ParamName,
ParamDisplay: v.ParamDisplay,
ParamType: v.ParamType,
OperaType: 0,
ParamJsonOld: hasItems[0].ParamJson,
ParamJsonNew: v.ParamJson,
}
r.ptNeConfigDataLogRepository.Insert(changeLog)
}
}
}
// SelectByStubType 通过存根类型查询
func (r *PtNeConfigDataService) SelectByStubType(param model.PtNeConfigData) model.PtNeConfigData {
list := r.SelectList(param)
if len(list) == 0 && param.StubType != "1" {
param.CreateBy = ""
param.StubType = "1"
list = r.SelectList(param)
}
if len(list) == 0 && param.StubType != "0" {
param.CreateBy = ""
param.StubType = "0"
param.DeptId = "100"
list = r.SelectList(param)
}
var paraData model.PtNeConfigData
if len(list) > 0 {
paraData = list[0]
if err := json.Unmarshal([]byte(paraData.ParamJson), &paraData.ParamData); err != nil {
paraData.ParamData = []map[string]any{}
}
}
return paraData
}
// ApplyToNe 参数应用到网元
func (r *PtNeConfigDataService) ApplyToNe(paramUser, neType string) error {
ptConfs := r.SelectList(model.PtNeConfigData{CreateBy: paramUser, NeType: neType})
if len(ptConfs) == 0 {
return fmt.Errorf("NeConfigData Not Found")
}
// 找网元只有一套就固定neId:001
neInfo := r.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, "001")
if neInfo.NeType != neType || neInfo.ID == "" {
return fmt.Errorf("NeInfo Not Found")
}
// 遍历去设置参数
for _, ptConf := range ptConfs {
if err := json.Unmarshal([]byte(ptConf.ParamJson), &ptConf.ParamData); err != nil {
ptConf.ParamData = []map[string]any{}
}
// 将json数据转字符串存储 [{},{}]
if len(ptConf.ParamData) == 0 {
continue
}
// 单层逐个更新
if ptConf.ParamType == "list" {
for k, v := range ptConf.ParamData[0] {
neFetchlink.NeConfigUpdate(neInfo, ptConf.ParamName, "", map[string]any{k: v})
}
}
// 多层逐个更新
if ptConf.ParamType == "array" {
// 删除原先配置
resData, err := neFetchlink.NeConfigInfo(neInfo, ptConf.ParamName)
if err != nil {
continue
}
if data, ok := resData["data"]; ok {
for i := 0; i < len(data.([]any)); i++ { // 倒序遍历
originIndex := i
newIndex := len(data.([]any)) - originIndex - 1
element := data.([]any)[newIndex]
loc := parse.Number(element.(map[string]any)["index"])
neFetchlink.NeConfigDelete(neInfo, ptConf.ParamName, fmt.Sprint(loc))
}
}
// 遍历新增
for _, v := range ptConf.ParamData {
loc := parse.Number(v["index"])
neFetchlink.NeConfigInstall(neInfo, ptConf.ParamName, fmt.Sprint(loc), v)
// 检查是否有array子层
for vk, vv := range v {
if reflect.TypeOf(vv).Kind() == reflect.Slice {
for i, item := range vv.([]any) {
data := item.(map[string]any)
data["index"] = i
neFetchlink.NeConfigInstall(neInfo, ptConf.ParamName, fmt.Sprintf("%v/%s/%v", loc, vk, i), data)
}
}
}
}
}
}
return nil
}
// ExportToExcel 导出网元的全部配置项数据
func (r *PtNeConfigDataService) ExportToExcel(operaUserName, fileName string) (string, error) {
confs := r.neConfigService.SelectNeConfigByNeType("*")
datas := r.ptNeConfigDataRepository.SelectList(model.PtNeConfigData{CreateBy: operaUserName})
return r.writeSheet(confs, datas, fileName)
}
// writeSheet 写入表格
func (r *PtNeConfigDataService) writeSheet(sheetData []neModel.NeConfig, cellData []model.PtNeConfigData, fileName string) (string, error) {
f := excelize.NewFile()
defer f.Close()
// 设置颜色填充
styleType, _ := f.NewStyle(&excelize.Style{Fill: excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{"F0F806"}, // RRGGBB 格式的
}})
styleRow, _ := f.NewStyle(&excelize.Style{Fill: excelize.Fill{
Type: "pattern",
Pattern: 1,
Color: []string{"D0CECE"}, // RRGGBB 格式的
}})
styleMore, _ := f.NewStyle(&excelize.Style{Font: &excelize.Font{
Color: "4472C4",
VertAlign: "center",
}})
// 网元工作簿行计数
sheetNameRows := make(map[string]int)
for _, sd := range sheetData {
_ = json.Unmarshal([]byte(sd.ParamJson), &sd.ParamData)
// 取到对应数据
var data model.PtNeConfigData
for _, cell := range cellData {
if cell.NeType == sd.NeType && cell.ParamName == sd.ParamName {
data = cell
break
}
}
if err := json.Unmarshal([]byte(data.ParamJson), &data.ParamData); err != nil || len(data.ParamData) == 0 {
continue
}
sheetName := sd.NeType
// 创建一个工作表
f.NewSheet(sheetName)
// 设置工作表上宽度为 20
// f.SetColWidth(sheetName, "B", "C", 20)
sheetRows := 1
if v, ok := sheetNameRows[sheetName]; ok && v != 0 {
sheetRows = v
}
// 合并标题单元格
f.MergeCell(sheetName, fmt.Sprintf("A%d", sheetRows), fmt.Sprintf("C%d", sheetRows))
f.SetCellStyle(sheetName, fmt.Sprintf("A%d", sheetRows), fmt.Sprintf("C%d", sheetRows), styleType)
// 标题
f.SetCellValue(sheetName, fmt.Sprintf("A%d", sheetRows), sd.ParamDisplay)
sheetRows += 1
// fmt.Println(sheetName, " ========= ", sd.ParamDisplay, " ==== ", sd.ParamType, len(data.ParamData))
itemData := []*[]any{}
if sd.ParamType == "list" {
itemData = append(itemData, &[]any{"Key", "Value"})
for _, v := range sd.ParamData {
name := v["name"].(string)
value := v["value"]
// 取到对应数据
for _, dv := range data.ParamData {
value = dv[name]
}
itemData = append(itemData, &[]any{v["display"], value})
}
}
if sd.ParamType == "array" {
rowTitle := []any{} // 行头
rowData := make([][]any, len(data.ParamData)) // 行数据
for _, v := range sd.ParamData {
rowTitle = append(rowTitle, v["display"])
name := v["name"].(string)
// 取到对应数据
for i, dv := range data.ParamData {
rowData[i] = append(rowData[i], dv[name])
}
}
itemData = append(itemData, &rowTitle)
for _, v := range rowData {
item := v
itemData = append(itemData, &item)
}
}
for i, row := range itemData {
// 行头样式
if i == 0 {
f.SetCellStyle(sheetName, fmt.Sprintf("A%d", sheetRows), fmt.Sprintf("A%d", sheetRows), styleRow)
}
f.SetSheetRow(sheetName, fmt.Sprintf("A%d", sheetRows), row)
// 存在array子层时转字符串
for i, s := range *row {
if reflect.ValueOf(s).Kind() == reflect.Slice {
if cell, err := excelize.CoordinatesToCellName(i+1, sheetRows); err == nil {
b, _ := json.Marshal(s)
f.SetCellDefault(sheetName, cell, "#")
f.SetCellStyle(sheetName, cell, cell, styleMore)
f.AddComment(sheetName, excelize.Comment{
Cell: cell,
Author: "OMC",
Text: string(b),
})
}
}
}
sheetRows += 1
}
// 记下次写入的行数
sheetRows += 2
sheetNameRows[sheetName] = sheetRows
}
// 默认工作表
f.SetCellValue("Sheet1", "A1", cellData[0].CreateBy)
f.MergeCell("Sheet1", "A1", "B1")
f.SetCellStyle("Sheet1", "A1", "B1", styleType)
f.SetCellValue("Sheet1", "A2", "5GC")
f.SetCellStyle("Sheet1", "A2", "A2", styleRow)
sheetNameData := []any{}
for k := range sheetNameRows {
sheetNameData = append(sheetNameData, k)
}
f.SetSheetRow("Sheet1", "A3", &sheetNameData)
// 上传资源路径
filePath := file.ParseUploadFileDir(uploadsubpath.EXPORT)
saveFilePath := filepath.ToSlash(filepath.Join(filePath, fileName))
// 创建文件目录
if err := os.MkdirAll(filepath.Dir(saveFilePath), 0775); err != nil {
return "", fmt.Errorf("failed to create save file %v", err)
}
// 根据指定路径保存文件
if err := f.SaveAs(saveFilePath); err != nil {
return "", fmt.Errorf("failed to save worksheet %v", err)
}
return saveFilePath, nil
}