123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- 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
- }
|