package calculate import ( "errors" "reflect" "strconv" "time" "zhiyuan/models/calc" "zhiyuan/pkg/config" "zhiyuan/pkg/db" "zhiyuan/pkg/utils" "github.com/gin-gonic/gin" "github.com/tealeg/xlsx/v3" ) type ProductFrom struct { ID int64 `json:"id" label:"ID" binding:"required"` Name string `json:"name" label:"产品名称" binding:"required"` Spec map[string]string `json:"spec" label:"规格"` } type ItemFrom struct { ID int64 `json:"id" label:"ID" binding:"required"` Items []ItemFrom `json:"items" label:"项目""` Product ProductFrom `json:"product" label:"产品""` Prop map[int64]string `json:"prop" label:"属性"` Results map[int64]interface{} `json:"results" label:"结果"` } type OrderFrom struct { ID int64 `json:"id" label:"ID" binding:"required"` Items []ItemFrom `json:"items" label:"项目""` Prop map[int64]string `json:"prop" label:"属性"` Results map[int64]interface{} `json:"results" label:"结果"` } type OrderItem struct { OrderNumber int64 `json:"order_number" label:"序号"` Item *calc.Item `json:"item" label:"项目"` From *ItemFrom `json:"from" label:"表单"` Product *calc.Product `json:"product" label:"产品"` Childs []*OrderItem `json:"childs" label:"子项目""` Parent *OrderItem `json:"parent" label:"父项目""` } type Order struct { ID int64 `json:"id" label:"ID"` AdminId int64 `json:"adminId" label:"AdminID"` Calc *calc.Calculate `json:"calculate" label:"核算"` From *OrderFrom `json:"from" label:"表单"` Items []*OrderItem `json:"items" label:"项目""` } type OrderAbstract struct { Prop map[string]string `json:"prop" label:"属性"` Results map[string]string `json:"results" label:"结果"` } func (item *OrderItem) GetFrom(getall bool) (from ItemFrom) { from.ID = item.Item.ID from.Product = item.From.Product from.Prop = make(map[int64]string) for _, prop := range item.Item.Props { if (prop.Type == 2 || prop.Type == 3) && (getall || prop.State == 1) { from.Prop[prop.ID] = item.From.Prop[prop.ID] } } from.Results = make(map[int64]interface{}) for _, prop := range item.Item.Props { if prop.Type == 1 && (getall || prop.State == 1) { from.Results[prop.ID] = item.From.Results[prop.ID] } } from.Items = make([]ItemFrom, 0) for _, v := range item.Childs { if getall || v.Item.State == 1 { from.Items = append(from.Items, v.GetFrom(getall)) } } return } func (order *Order) GetFrom(getall bool) (from OrderFrom) { from.ID = order.Calc.ID from.Prop = make(map[int64]string) for _, prop := range order.Calc.Props { if (prop.Type == 2 || prop.Type == 3) && (getall || prop.State == 1) { from.Prop[prop.ID] = order.From.Prop[prop.ID] } } from.Results = make(map[int64]interface{}) for _, prop := range order.Calc.Props { if prop.Type == 1 && (getall || prop.State == 1) { from.Results[prop.ID] = order.From.Results[prop.ID] } } from.Items = make([]ItemFrom, 0) for _, v := range order.Items { if getall || v.Item.State == 1 { from.Items = append(from.Items, v.GetFrom(getall)) } } return } func propVerify(props []calc.Property, from map[int64]string) error { for _, v := range props { if v.Type == 2 || v.Type == 3 { if _, ok := from[v.ID]; !ok { return errors.New("属性未填: " + v.Name) } if v.DataType == "number" { if _, err := strconv.ParseFloat(from[v.ID], 64); err != nil { return errors.New("属性验证失败: " + v.Name) } } if v.DataType == "array" { var array interface{} utils.JsonDecode(from[v.ID]).To(&array) if _, ok := db.ToArray(array); !ok { return errors.New("属性验证失败: " + v.Name) } } } } return nil } func productVerify(item *calc.Item, from ProductFrom, create bool) (*calc.Product, error) { if item.TypeId == 0 && from.ID == 0 { return nil, nil } if create { if item.TypeId == 0 || from.ID == 0 { return nil, errors.New("产品选择错误: " + item.Name) } } var product *calc.Product db.GetModel(map[string]interface{}{"id": from.ID}, &product) if create { if product == nil || product.ID == 0 { return nil, errors.New("产品不存在: " + item.Name) } if product.TypeId != item.TypeId { return nil, errors.New("产品类型错误: " + item.Name) } } return product, nil } func createOrderItem(item *calc.Item, from *ItemFrom, create bool) (*OrderItem, error) { if from == nil { from = new(ItemFrom) from.Prop = make(map[int64]string) } if create { from.Results = make(map[int64]interface{}) err := propVerify(item.Props, from.Prop) if err != nil { return nil, err } } product, err := productVerify(item, from.Product, create) if err != nil { return nil, err } order := new(OrderItem) items, err := itemVerify(item.Childs, from.Items, order, create) if err != nil { return nil, err } order.Item = item order.From = from order.Product = product order.Childs = items if order.Product != nil && order.Product.ID != 0 { order.From.Product.Name = order.Product.Name } return order, nil } func itemVerify(items []calc.Item, froms []ItemFrom, parent *OrderItem, create bool) ([]*OrderItem, error) { orderItems := make([]*OrderItem, 0) orderNumber := int64(0) for key, item := range items { if create && item.State == 0 { for n := int64(0); n < item.Min; n++ { orderItem, err := createOrderItem(&items[key], nil, create) if err != nil { return nil, err } orderItem.Parent = parent orderItem.OrderNumber = orderNumber orderItems = append(orderItems, orderItem) orderNumber++ } } else { var n int64 = 0 for i, from := range froms { if from.ID == item.ID { orderItem, err := createOrderItem(&items[key], &froms[i], create) if err != nil { return nil, err } orderItem.Parent = parent orderItem.OrderNumber = orderNumber orderItems = append(orderItems, orderItem) orderNumber++ n++ } } if create && (n < item.Min || n > item.Max) { return nil, errors.New("项目数量错误: " + item.Name) } } } return orderItems, nil } func (from *OrderFrom) BindOrder(calc *calc.Calculate, create bool) (*Order, error) { if calc.ID != from.ID { return nil, errors.New("核算错误") } if create { from.Results = make(map[int64]interface{}) err := propVerify(calc.Props, from.Prop) if err != nil { return nil, err } } order := new(Order) order.Calc = calc order.From = from items, err := itemVerify(calc.Items, from.Items, nil, create) if err != nil { return nil, err } order.Items = items return order, nil } func (from *OrderFrom) LoadOrder(create bool) (*Order, error) { calc := calc.GetCalculateModel(from.ID, true) if calc == nil { return nil, errors.New("核算不存在") } return from.BindOrder(calc, create) } func (from *OrderFrom) CreateOrder() (*Order, error) { return from.LoadOrder(true) } func (order *Order) Abstract() (abstract OrderAbstract) { abstract.Prop = make(map[string]string) abstract.Results = make(map[string]string) for _, prop := range order.Calc.Props { if (prop.Type == 2 || prop.Type == 3) && prop.State == 1 { if ret, ok := ToDataType(order.From.Prop[prop.ID], prop.DataType); ok { abstract.Prop[prop.Name] = db.ToString(ret) } } } for _, prop := range order.Calc.Props { if prop.Type == 1 && prop.State == 1 { if ret, ok := ToDataType(order.From.Results[prop.ID], prop.DataType); ok { abstract.Results[prop.Name] = db.ToString(ret) } } } return } func BindOrder(calc *calc.Calculate, model calc.Order) (*Order, error) { from := new(OrderFrom) utils.JsonDecode(model.Content).To(from) order, err := from.BindOrder(calc, false) if err != nil { return nil, err } order.ID = model.ID order.AdminId = model.AdminId return order, nil } func LoadOrder(model calc.Order) (*Order, error) { from := new(OrderFrom) utils.JsonDecode(model.Content).To(from) order, err := from.LoadOrder(false) if err != nil { return nil, err } order.ID = model.ID order.AdminId = model.AdminId return order, nil } func (order *Order) Save() error { data := map[string]interface{}{ "calcId": order.Calc.ID, "adminId": order.AdminId, "abstract": utils.JsonEncode(order.Abstract()), "content": utils.JsonEncode(order.GetFrom(true)), } if order.ID == 0 { id, err := db.InsertModel(reflect.TypeOf(calc.Order{}), data) if err != nil { return err } order.ID = id } else { err := db.UpdateModel(reflect.TypeOf(calc.Order{}), order.ID, data) if err != nil { return err } } return nil } type exportContext struct { value string item *OrderItem } func runValue(begin string, formula []string, context *Context) []exportContext { if len(formula) == 0 { return []exportContext{{begin, context.Item}} } else if len(formula) == 1 { ret, err := context.EvaluableExpression(formula[0]) if err == nil { begin += db.ToString(ret) } return []exportContext{{begin, context.Item}} } results := make([]exportContext, 0) items := context.Order.Items if context.Item != nil { items = context.Item.Childs } orderNumber := int64(0) for i, _ := range items { if formula[0] != "" { ret, err := context.Exec(formula[0], items[i], int64(i)) if err != nil { continue } if retb, ok := ret.(bool); !ok || !retb { continue } } context.Push(items[i]) context.OrderNumber = orderNumber for _, c := range runValue(begin, formula[1:], context) { c.value = begin + c.value results = append(results, c) } context.Pop() orderNumber++ } return results } func runContext(contexts []exportContext, value string, context *Context) []exportContext { results := make([]exportContext, 0) for i, v := range contexts { context.Push(v.item) context.OrderNumber = int64(i) for _, c := range readValue(value, context) { c.value = v.value + c.value results = append(results, c) } context.Pop() } return results } func readValue(value string, context *Context) []exportContext { result := "" brackets := false begin := "" formula := make([]string, 0) for i := 0; i < len([]rune(value)); i++ { switch []rune(value)[i] { case '\\': if i+1 < len([]rune(value)) { if []rune(value)[i+1] == '{' || []rune(value)[i+1] == '}' || []rune(value)[i+1] == ';' { result += string([]rune(value)[i+1]) i++ } else { result += string([]rune(value)[i]) } } else { result += string([]rune(value)[i]) } case '{': if brackets { result += string([]rune(value)[i]) } else { begin = result result = "" brackets = true } case '}': if brackets { formula = append(formula, result) result = "" return runContext(runValue(begin, formula, context), string([]rune(value)[i+1:]), context) } else { result += string([]rune(value)[i]) } case ';': if brackets { formula = append(formula, result) result = "" } else { result += string([]rune(value)[i]) } default: result += string([]rune(value)[i]) } } if brackets { formula = append(formula, result) result = "" return runValue(begin, formula, context) } return []exportContext{{result, context.Item}} } func readCells(cells []*xlsx.Cell, context *Context) [][]string { rows := make([][]string, 0) if len(cells) == 0 { rows = append(rows, []string{}) return rows } for i, c := range readValue(cells[0].String(), context) { context.Push(c.item) context.OrderNumber = int64(i) for _, s := range readCells(cells[1:], context) { rows = append(rows, append([]string{c.value}, s...)) } context.Pop() } return rows } func (order *Order) Export(filename string, c *gin.Context) string { template, err := xlsx.OpenFile(filename) if err != nil { return "" } export := xlsx.NewFile() for sheetName, sheet := range template.Sheet { sh, err := export.AddSheet(sheetName) if err != nil { return "" } sheet.Cols.ForEach(func(idx int, col *xlsx.Col) { sh.SetColWidth(col.Min, col.Max, *col.Width) }) err = sheet.ForEachRow(func(r *xlsx.Row) error { cells := make([]*xlsx.Cell, 0) err := r.ForEachCell(func(c *xlsx.Cell) error { cells = append(cells, c) return nil }) if err != nil { return err } context := NewContext(order, c) for _, rowStrings := range readCells(cells, context) { row := sh.AddRow() row.SetHeight(r.GetHeight()) row.SetOutlineLevel(r.GetOutlineLevel()) for i, cellStrings := range rowStrings { c := row.AddCell() binary, _ := cells[i].MarshalBinary() c.UnmarshalBinary(binary) c.SetStyle(cells[i].GetStyle()) c.Value = cellStrings } } return nil }) if err != nil { return "" } } exportFileName := utils.ToStr(time.Now().UnixNano()) + ".xlsx" if err := export.Save(config.Cfg.App.ExportPath + exportFileName); err != nil { return "" } return exportFileName }