wangchuanjin 1 mese fa
commit
63b4b89c53
14 ha cambiato i file con 820 aggiunte e 0 eliminazioni
  1. BIN
      .exe
  2. 90 0
      config.yaml
  3. 35 0
      db/db.go
  4. BIN
      extract
  5. 47 0
      go.mod
  6. 111 0
      go.sum
  7. BIN
      go_build_dataIdentify.exe
  8. BIN
      go_build_dataIdentify_linux
  9. 13 0
      logs/dataIdentify.log
  10. 33 0
      main.go
  11. 170 0
      main_test.go
  12. 73 0
      service/model.go
  13. 151 0
      service/rule.go
  14. 97 0
      service/service.go

BIN
.exe


+ 90 - 0
config.yaml

@@ -0,0 +1,90 @@
+port: ":8811"
+poolSize: 4
+logger:
+  filename: "./logs/dataIdentify.log"
+  maxSize: 1024
+  maxBackups: 3
+  maxAge: 3
+  compress: true
+mongodb:
+  main:
+    #mongodbAddr: "172.20.45.128:27080,172.31.31.202:27081"
+    #dbName: "qfw"
+    #userName: "wcj2025"
+    #password: "Bidingjy@0522"
+    size: 8
+    mongodbAddr: "172.20.45.129:27002"
+    dbName: "qfw_data"
+    collection: "bidding_hasdetail"
+  extract:
+    mongodbAddr: "172.17.4.85:27080"
+    dbName: "qfw"
+    size: 8
+model:
+  apiServer: "https://dify_v2.jydev.jianyu360.com/v1"
+  apiKey: "app-a2sCpnnOn6UAhWj9waToxETv"
+  user: "jianyu"
+clearPatterns:
+  - "第[^一]中标候选人.+"
+quoteMode:
+  rules:
+    - mode: "费率模式"
+      patterns:
+        - "((投标|中标|成交).{0,8}费率)|((投标报价系数|费率报价)[0-9.]+%)"
+    - mode: "上浮下浮模式"
+      patterns:
+        - "(?s)(投标|中标|成交).{0,6}[::]?.{0,6}[上下]浮率"
+        - "(?s)统一[上下]浮率为[::][0-9.]+%"
+        - "(?s)存款利率[上下]浮率[::]?[0-9.]+%"
+    - mode: "单价模式"
+      patterns:
+        - "全部包含:数量+单价+规格型号"
+    - mode: "整标报价模式"
+      patterns:
+        - "(中标|成交)总价"
+  modelPatterns:
+    - "上浮率|下浮率|费率"
+    - "全部包含:单价+数量"
+bidCommonwealth:
+  subtype:
+    - 中标
+    - 成交
+    - 合同
+  showOnlyOnce:
+    - ["牵头(羊|人)","成员单位"]
+  firstWinnerOrder: "联合体|牵头(人|羊)"
+  # 白名单规则集合(确认是联合体中标)
+  whitelistPatterns:
+    - "由.*组成联合体.*中标"
+    - "中标人为联合体.*包含.*?。"
+    - "联合体.*中标候选人.*包括.*?。"
+    - "联合体.*中标.*(?:成员|牵头方|牵头人|成员单位).*为.*?。"
+    - "牵头单位.*与.*组成.*联合体.*中标"
+    - "联合体.*为唯一中标候选人"
+    - "联合体.*通过评审.*被推荐为中标人"
+    - "联合体.*(?:为|成为|被推荐为|被确定为).*中标人"
+    - "中标结果:.*由.*组成.*联合体"
+    - "中标单位:.*公司.*与.*公司.*组成联合体"
+    - "中标人信息.*包含.*联合体.*成员"
+    - "评标委员会推荐.*联合体.*为中标候选人"
+    - "是否联合体[::]+是"
+    - "(((中标|成交)[^候选]+?)|(第一.{0,6}候选人))[::].+(联合|主)体"
+    - "联合体.{0,8}、.{2,20}联合体"
+    - ".{2,50}与.{2,50}组成的联合体"
+  # 黑名单规则集合(排除非联合体中标)
+  blacklistPatterns:
+    - "不[允许|接受|支持]联合体"
+    - "拒绝联合体投标"
+    - "仅限独立投标人"
+    - "中标单位为.*公司$"
+    - "联合体.*未中标"
+    - "联合体.*未通过资格审查"
+    - "中标单位.*与.*无关"
+    - "中标单位.*但.*未组成联合体"
+    - "仅限.*独立.*投标"
+    - "该项目.*不允许联合体"
+    - "联合体.*不符合.*资格要求"
+    - "中标人.*未与其他单位合作"
+    - "单一投标人.*中标"
+  modelPatterns:
+    - "联合体(牵头人|成员)|牵头人|成员单位"

+ 35 - 0
db/db.go

@@ -0,0 +1,35 @@
+package db
+
+import (
+	. "app.yhyue.com/moapp/jybase/mongodb"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"log"
+)
+
+var (
+	Mgo_Main    *MongodbSim
+	Mgo_Extract *MongodbSim
+)
+
+func init() {
+	ctx := gctx.New()
+	Mgo_Main = &MongodbSim{
+		MongodbAddr: g.Config().MustGet(ctx, "mongodb.main.mongodbAddr").String(),
+		Size:        g.Config().MustGet(ctx, "mongodb.main.size").Int(),
+		DbName:      g.Config().MustGet(ctx, "mongodb.main.dbName").String(),
+		UserName:    g.Config().MustGet(ctx, "mongodb.main.userName").String(),
+		Password:    g.Config().MustGet(ctx, "mongodb.main.password").String(),
+	}
+	Mgo_Main.InitPool()
+	log.Println("初始化 mongodb main")
+	Mgo_Extract = &MongodbSim{
+		MongodbAddr: g.Config().MustGet(ctx, "mongodb.extract.mongodbAddr").String(),
+		Size:        g.Config().MustGet(ctx, "mongodb.extract.size").Int(),
+		DbName:      g.Config().MustGet(ctx, "mongodb.extract.dbName").String(),
+		UserName:    g.Config().MustGet(ctx, "mongodb.extract.userName").String(),
+		Password:    g.Config().MustGet(ctx, "mongodb.extract.password").String(),
+	}
+	Mgo_Extract.InitPool()
+	log.Println("初始化 mongodb extract")
+}

BIN
extract


+ 47 - 0
go.mod

@@ -0,0 +1,47 @@
+module dataIdentify
+
+go 1.23.7
+
+toolchain go1.23.10
+
+require (
+	app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b
+	github.com/gogf/gf/v2 v2.7.0
+	github.com/safejob/dify-sdk-go v1.4.3-rc.1
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1
+)
+
+require (
+	github.com/BurntSushi/toml v1.2.0 // indirect
+	github.com/clbanning/mxj/v2 v2.7.0 // indirect
+	github.com/fatih/color v1.15.0 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/go-logr/logr v1.2.3 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-stack/stack v1.8.0 // indirect
+	github.com/golang/snappy v0.0.1 // indirect
+	github.com/google/go-cmp v0.6.0 // indirect
+	github.com/gorilla/websocket v1.5.0 // indirect
+	github.com/grokify/html-strip-tags-go v0.0.1 // indirect
+	github.com/klauspost/compress v1.13.6 // indirect
+	github.com/magiconair/properties v1.8.6 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.17 // indirect
+	github.com/mattn/go-runewidth v0.0.9 // indirect
+	github.com/olekukonko/tablewriter v0.0.5 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.0.2 // indirect
+	github.com/xdg-go/stringprep v1.0.2 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	go.mongodb.org/mongo-driver v1.9.1 // indirect
+	go.opentelemetry.io/otel v1.14.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.14.0 // indirect
+	go.opentelemetry.io/otel/trace v1.14.0 // indirect
+	golang.org/x/crypto v0.37.0 // indirect
+	golang.org/x/net v0.39.0 // indirect
+	golang.org/x/sync v0.13.0 // indirect
+	golang.org/x/sys v0.32.0 // indirect
+	golang.org/x/text v0.24.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 111 - 0
go.sum

@@ -0,0 +1,111 @@
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b h1:bQKiTTrlbDIc+Z2y7ynbZrieaaAUB2CisE/rl42FlPY=
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b/go.mod h1:OEtMbsn7wY/7MLgV7yDUpVDKExUoj3B8h+4w4ZckJQQ=
+github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
+github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
+github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogf/gf/v2 v2.7.0 h1:CjxhbMiE7oqf6K8ZtGuKt3dQEwK4vL6LhiI+dI7tJGU=
+github.com/gogf/gf/v2 v2.7.0/go.mod h1:Qu8nimKt9aupJQcdUL85tWF4Mfxocz97zUt8UC4abVI=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
+github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/safejob/dify-sdk-go v1.4.3-rc.1 h1:NxVbuQZEYpoMLHNkc28wCG6B0+9DkQZzyBQ7d+n7bRo=
+github.com/safejob/dify-sdk-go v1.4.3-rc.1/go.mod h1:uOjNfuk/UUd+NI3nbkhNLzmYPR747Spqds1E2LznDPQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
+go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
+go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
+go.opentelemetry.io/otel/sdk v1.14.0 h1:PDCppFRDq8A1jL9v6KMI6dYesaq+DFcDZvjsoGvxGzY=
+go.opentelemetry.io/otel/sdk v1.14.0/go.mod h1:bwIC5TjrNG6QDCHNWvW4HLHtUQ4I+VQDsnjhvyZCALM=
+go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
+go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
+golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
+golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

BIN
go_build_dataIdentify.exe


BIN
go_build_dataIdentify_linux


+ 13 - 0
logs/dataIdentify.log

@@ -0,0 +1,13 @@
+2025/07/05 17:37:48 rpc server started on :8811
+2025/07/05 17:38:07 rpc接收到要识别的_id 0xc000248920
+2025/07/05 17:38:07 上浮率|下浮率|费率
+2025/07/05 17:38:09 60146f837cdc9beb564097a9 大模型 map[中标联合体:<nil> 报价模式:上浮下浮模式]
+2025/07/05 17:38:09 60146f837cdc9beb564097a9 规则 报价模式 上浮下浮模式 中标联合体 -1
+2025/07/05 17:38:53 rpc接收到要识别的_id 0xc000620cb0
+2025/07/05 17:38:53 上浮率|下浮率|费率
+2025/07/05 17:38:55 60146f837cdc9beb564097a9 大模型 map[中标联合体:<nil> 报价模式:上浮下浮模式]
+2025/07/05 17:38:55 60146f837cdc9beb564097a9 规则 报价模式 上浮下浮模式 中标联合体 -1
+2025/07/05 17:41:00 rpc接收到要识别的_id 0xc000497320
+2025/07/05 17:41:00 上浮率|下浮率|费率
+2025/07/05 17:41:02 60146f837cdc9beb564097a9 大模型 map[中标联合体:<nil> 报价模式:上浮下浮模式]
+2025/07/05 17:41:02 60146f837cdc9beb564097a9 规则 报价模式 上浮下浮模式 中标联合体 -1

+ 33 - 0
main.go

@@ -0,0 +1,33 @@
+package main
+
+import (
+	. "dataIdentify/service"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"gopkg.in/natefinch/lumberjack.v2"
+	"io"
+	"log"
+	"net/http"
+	"net/rpc"
+	"os"
+)
+
+func main() {
+	var logger *lumberjack.Logger
+	ctx := gctx.New()
+	g.Config().MustGet(ctx, "logger").Struct(&logger)
+	writers := []io.Writer{logger}
+	if g.Config().MustGet(ctx, "logger.console").Bool() {
+		writers = append(writers, os.Stdout)
+	}
+	// 启动RPC服务
+	rpcService := new(DataIdentify)
+	rpc.Register(rpcService)
+	rpc.HandleHTTP()
+	port := g.Config().MustGet(ctx, "port").String()
+	log.Println("rpc server started on " + port)
+	if err := http.ListenAndServe(port, nil); err != nil {
+		log.Fatalln("rpc server error", err)
+	}
+
+}

+ 170 - 0
main_test.go

@@ -0,0 +1,170 @@
+package main
+
+import (
+	"app.yhyue.com/moapp/jybase/encrypt"
+	. "app.yhyue.com/moapp/jybase/mongodb"
+	. "dataIdentify/db"
+	. "dataIdentify/service"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"log"
+	"net/rpc"
+	"strings"
+	"sync"
+	"testing"
+)
+
+const (
+	maxSize = 100
+)
+
+//	{
+//		$unset: {
+//		"bid_commonwealth" : "",
+//		"quote_mode" : "",
+//		"model_dataidentify" : ""
+//		}
+//	}
+func TestAddField(t *testing.T) {
+	pool := make(chan bool, 5)
+	wait := &sync.WaitGroup{}
+	sess := Mgo_Main.GetMgoConn()
+	defer Mgo_Main.DestoryMongoConn(sess)
+	it := sess.DB("qfw_data").C("wcj_bidding").Find(map[string]interface{}{}).Select(nil).Sort("-_id").Iter()
+	index := 0
+	for tm := make(map[string]interface{}); it.Next(tm); {
+		index++
+		if index%100 == 0 {
+			log.Println("index", index)
+		}
+		pool <- true
+		wait.Add(1)
+		go func(m map[string]interface{}) {
+			defer func() {
+				<-pool
+				wait.Done()
+			}()
+			href := "https://www.jianyu360.com/nologin/content/" + encrypt.CommonEncodeArticle("content", BsonIdToSId(m["_id"])) + ".html"
+			Mgo_Main.Update("wcj_bidding", map[string]interface{}{
+				"_id": m["_id"],
+			}, map[string]interface{}{"$set": map[string]interface{}{
+				"href": href,
+			}}, false, false)
+		}(tm)
+		tm = make(map[string]interface{})
+	}
+	wait.Wait()
+	log.Println("over...", index)
+}
+
+// 示例测试
+func TestRule(t *testing.T) {
+	Start("5b0e1b8da5cb26b9b7b8c9ac")
+}
+func TestAll(t *testing.T) {
+	pool := make(chan bool, g.Config().MustGet(gctx.New(), "poolSize").Int())
+	wait := &sync.WaitGroup{}
+	sess := Mgo_Main.GetMgoConn()
+	defer Mgo_Main.DestoryMongoConn(sess)
+	it := sess.DB("qfw_data").C("wcj_bidding").Find(map[string]interface{}{
+		"_id": StringTOBsonId("67c123333309c0998b619793"),
+	}).Select(SelectField).Sort("-_id").Iter()
+	index := 0
+	for tm := make(map[string]interface{}); it.Next(tm); {
+		index++
+		if index%100 == 0 {
+			log.Println("index", index)
+		}
+		pool <- true
+		wait.Add(1)
+		go func(m map[string]interface{}) {
+			defer func() {
+				<-pool
+				wait.Done()
+			}()
+			_id := BsonIdToSId(m["_id"])
+			flag, quoteMode, bidCommonwealth, other := Pretreatment(_id, m)
+			if !flag {
+				return
+			}
+			set := map[string]interface{}{}
+			if quoteMode != "" {
+				set["quote_mode"] = quoteMode
+			}
+			if bidCommonwealth != -1 {
+				set["bid_commonwealth"] = bidCommonwealth
+			}
+			if set != nil {
+				set["model_dataidentify"] = other
+			}
+			if len(set) > 0 {
+				Mgo_Main.UpdateById("wcj_bidding", m["_id"], map[string]interface{}{"$set": set})
+			}
+		}(tm)
+		tm = make(map[string]interface{})
+	}
+	wait.Wait()
+	log.Println("over...", index)
+}
+func TestExtract(t *testing.T) {
+	log.Println("start...")
+	sess := Mgo_Main.GetMgoConn()
+	defer Mgo_Main.DestoryMongoConn(sess)
+	it := sess.DB("qfw_data").C("bidding_hasdetail").Find(map[string]interface{}{
+		"_id": map[string]interface{}{
+			"$lt": StringTOBsonId("68639706408dd61d6e5d05cd"),
+		},
+	}).Select(map[string]interface{}{
+		"detail":    1,
+		"_id":       1,
+		"subtype":   1,
+		"toptype":   1,
+		"s_winner":  1,
+		"bidamount": 1,
+	}).Sort("-_id").Iter()
+	types := "邀标、竞谈、单一、招标、中标、成交、合同"
+	all := map[string]int{}
+	for _, v := range strings.Split(types, "、") {
+		all[v] = 0
+	}
+	var isOver = func() bool {
+		for _, v := range all {
+			if v < maxSize {
+				return false
+			}
+		}
+		return true
+	}
+	index := 0
+	for m := make(map[string]interface{}); it.Next(m); {
+		index++
+		if index%500 == 0 {
+			log.Println("index", index)
+		}
+		subtype, _ := m["subtype"].(string)
+		if v, ok := all[subtype]; !ok || v >= maxSize {
+			continue
+		}
+		_id := Mgo_Main.SaveByOriID("wcj_bidding", m)
+		log.Println("save", _id)
+		all[subtype]++
+		if isOver() {
+			break
+		}
+	}
+	log.Println("over...", index)
+}
+func TestRpc(t *testing.T) {
+	conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8811")
+	if err != nil {
+		log.Println(err)
+		return
+	}
+	defer conn.Close()
+	var reply map[string]string
+	err = conn.Call("DataIdentify.Execute", "5b0dfeb4a5cb26b9b79c1330", &reply)
+	if err != nil {
+		log.Println(err)
+	}
+	log.Println(reply)
+}

+ 73 - 0
service/model.go

@@ -0,0 +1,73 @@
+package service
+
+import (
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"github.com/safejob/dify-sdk-go"
+	"github.com/safejob/dify-sdk-go/types"
+	"log"
+)
+
+type Model struct {
+}
+
+func (ml *Model) Execute(b *BidInfo) (bool, string, int, map[string]interface{}) {
+	return ml.Do(b, 2)
+}
+func (ml *Model) Do(b *BidInfo, t int) (bool, string, int, map[string]interface{}) {
+	result := ml.post(b.Id, b.Detail, t)
+	if result == nil {
+		return false, "", 0, nil
+	}
+	for k, v := range result {
+		if v == nil {
+			delete(result, k)
+		}
+	}
+	set := map[string]interface{}{}
+	flag := false
+	quoteMode, _ := result["报价模式"].(string)
+	if !allQuoteMode[quoteMode] {
+		quoteMode = ""
+		flag = true
+	}
+	bidCommonwealth := 0
+	if result["中标联合体"] != nil {
+		if result["中标联合体"] == "是" {
+			bidCommonwealth = 1
+		} else {
+			bidCommonwealth = -1
+		}
+		if result["中标联合体"] != "是" && result["中标联合体"] != "否" {
+			flag = true
+		}
+	}
+	if flag && result != nil {
+		set["model_dataidentify"] = result
+	}
+	return true, quoteMode, bidCommonwealth, set
+}
+func (ml *Model) post(_id, detail string, t int) map[string]interface{} {
+	ctx := gctx.New()
+	client, err := dify.NewClient(dify.ClientConfig{
+		ApiServer: g.Config().MustGet(ctx, "model.apiServer").String(),
+		ApiKey:    g.Config().MustGet(ctx, "model.apiKey").String(),
+		User:      g.Config().MustGet(ctx, "model.user").String(),
+	})
+	if err != nil {
+		log.Println(_id, "%v :Error running client: %v", err)
+		return nil
+	}
+	resp, err := client.WorkflowApp().RunBlock(ctx, types.WorkflowRequest{
+		Inputs: map[string]interface{}{
+			"detail": detail,
+			"type":   t,
+		},
+	})
+	if err != nil {
+		log.Println(_id, "%v :Error running client: %v", err)
+		return nil
+	}
+	log.Println(_id, "大模型", resp.Data.Outputs)
+	return resp.Data.Outputs
+}

+ 151 - 0
service/rule.go

@@ -0,0 +1,151 @@
+package service
+
+import (
+	"github.com/gogf/gf/v2/container/gvar"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"github.com/gogf/gf/v2/util/gconv"
+	"log"
+	"regexp"
+	"strings"
+)
+
+var (
+	clearPatterns                     = g.Config().MustGet(gctx.New(), "clearPatterns").Strings()
+	bidCommonwealth_blacklistPatterns = g.Config().MustGet(gctx.New(), "bidCommonwealth.blacklistPatterns").Strings()
+	bidCommonwealth_whitelistPatterns = g.Config().MustGet(gctx.New(), "bidCommonwealth.whitelistPatterns").Strings()
+	bidCommonwealth_modelPatterns     = g.Config().MustGet(gctx.New(), "bidCommonwealth.modelPatterns").Strings()
+	bidCommonwealth_firstWinnerOrder  = g.Config().MustGet(gctx.New(), "bidCommonwealth.firstWinnerOrder").String()
+	quoteModeRules                    = g.Config().MustGet(gctx.New(), "quoteMode.rules").Maps()
+	quoteMode_modelPatterns           = g.Config().MustGet(gctx.New(), "quoteMode.modelPatterns").Strings()
+	allQuoteMode                      = map[string]bool{}
+	showOnlyOnce                      = [][]string{}
+)
+
+func init() {
+	for _, v := range g.Config().MustGet(gctx.New(), "bidCommonwealth.showOnlyOnce").Array() {
+		showOnlyOnce = append(showOnlyOnce, gconv.Strings(v))
+	}
+	for _, v := range quoteModeRules {
+		vv := gvar.New(v).MapStrVar()
+		allQuoteMode[vv["mode"].String()] = true
+	}
+}
+
+type Rule struct{}
+
+// 判断是否是联合体中标
+func (r *Rule) Execute(b *BidInfo) (bool, string, int, map[string]interface{}) {
+	bc := r.bidCommonwealth(b)
+	quoteMode := r.quoteMode(b)
+	if bc == -2 && quoteMode == "大模型识别" {
+		_, quoteMode, bc, _ = (&Model{}).Do(b, 2)
+	} else if bc == -2 {
+		_, _, bc, _ = (&Model{}).Do(b, 3)
+	} else if quoteMode == "大模型识别" {
+		_, quoteMode, _, _ = (&Model{}).Do(b, 1)
+	}
+	if (quoteMode == "" || quoteMode == "无法识别") && b.Bidamount > 0 {
+		quoteMode = "整体报价模式"
+	}
+	log.Println(b.Id, "规则", "报价模式", quoteMode, "中标联合体", bc)
+	return true, quoteMode, bc, nil
+}
+
+// 识别中标联合体
+func (r *Rule) quoteMode(b *BidInfo) string {
+	for _, line := range strings.Split(b.Detail, "\n") {
+		for _, v := range quoteModeRules {
+			vv := gvar.New(v).MapStrVar()
+			if r.matchAnyPattern(line, vv["patterns"].Strings()) {
+				return vv["mode"].String()
+			}
+		}
+	}
+	if r.matchAnyPattern(b.Detail, quoteMode_modelPatterns) {
+		return "大模型识别"
+	}
+	return ""
+}
+
+// 识别中标联合体
+func (r *Rule) bidCommonwealth(b *BidInfo) int {
+	if len(b.WinnerOrder) > 0 {
+		if regexp.MustCompile(bidCommonwealth_firstWinnerOrder).MatchString(b.WinnerOrder[0]) {
+			return 1
+		}
+		return -1
+	}
+	for _, v := range strings.Split(b.KvText, "\n") {
+		if r.matchAnyPattern(v, bidCommonwealth_whitelistPatterns) {
+			return 1
+		}
+	}
+	v := b.Detail
+	// Step 1: 排除黑名单
+	if r.matchAnyPattern(v, bidCommonwealth_blacklistPatterns) {
+		return -1
+	}
+	if r.matchOnlyOnce(v) {
+		return 1
+	}
+	// Step 2: 精准匹配白名单
+	if r.matchAnyPattern(v, bidCommonwealth_whitelistPatterns) {
+		return 1
+	}
+	// Step 3: 检查“中标”附近是否有“联合体”
+	index := strings.Index(v, "中标")
+	if index != -1 {
+		start := max(0, index-50)
+		end := min(len(v), index+50)
+		contextAroundWin := v[start:end]
+		if strings.Contains(contextAroundWin, "联合体") {
+			return 1
+		}
+	}
+	if r.matchAnyPattern(v, bidCommonwealth_modelPatterns) {
+		return -2
+	}
+	return -1
+}
+
+// 匹配任意模式
+func (r *Rule) matchAnyPattern(text string, patterns []string) bool {
+	for _, pattern := range patterns {
+		if strings.HasPrefix(pattern, "全部包含:") {
+			vs := strings.Split(strings.TrimPrefix(pattern, "全部包含:"), "+")
+			index := 0
+			for _, v := range vs {
+				if strings.Contains(text, v) {
+					index++
+				}
+			}
+			if index == len(vs) {
+				log.Println(pattern)
+				return true
+			}
+			continue
+		}
+		if matched, _ := regexp.MatchString(pattern, text); matched {
+			log.Println(pattern)
+			return true
+		}
+	}
+	return false
+}
+
+func (r *Rule) matchOnlyOnce(text string) bool {
+	for _, v := range showOnlyOnce {
+		count := 0
+		for _, vv := range v {
+			if len(regexp.MustCompile(vv).FindAllString(text, -1)) != 1 {
+				break
+			}
+			count++
+		}
+		if count > 0 && count == len(v) {
+			return true
+		}
+	}
+	return false
+}

+ 97 - 0
service/service.go

@@ -0,0 +1,97 @@
+package service
+
+import (
+	. "dataIdentify/db"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gctx"
+	"github.com/gogf/gf/v2/util/gconv"
+	"log"
+	"regexp"
+	"strings"
+)
+
+var (
+	SelectField = map[string]interface{}{
+		"_id":         1,
+		"winnerorder": 1,
+		"detail":      1,
+		"subtype":     1,
+		"bidamount":   1,
+	}
+	service Service = &Rule{}
+)
+
+type DataIdentify struct {
+}
+
+func (d *DataIdentify) Execute(_id *string, reply *map[string]string) error {
+	log.Println("rpc接收到要识别的_id", _id)
+	_, a, b, _ := Start(*_id)
+	*reply = map[string]string{}
+	if !allQuoteMode[a] {
+		a = "其他"
+	}
+	if a == "费率模式" {
+		a = "费率"
+	} else if a == "单价模式" {
+		a = "单价"
+	} else if a == "上浮下浮模式" {
+		a = "折扣率"
+	} else if a == "整体报价模式" {
+		a = "正常报价"
+	}
+	(*reply)["报价模式"] = a
+	if b == 1 {
+		(*reply)["中标联合体"] = "是"
+	} else {
+		(*reply)["中标联合体"] = "否"
+	}
+	return nil
+}
+
+type Service interface {
+	Execute(b *BidInfo) (bool, string, int, map[string]interface{})
+}
+type BidInfo struct {
+	Id, Detail, Subtype string
+	WinnerOrder         []string
+	KvText              string
+	Bidamount           float64
+}
+
+func Start(_id string) (bool, string, int, map[string]interface{}) {
+	data, ok := Mgo_Main.FindById(g.Config().MustGet(gctx.New(), "mongodb.main.collection").String(), _id, SelectField)
+	if !ok || data == nil || len(*data) == 0 {
+		log.Println(_id, "没有找到标讯")
+		return false, "", 0, nil
+	}
+	return Pretreatment(_id, *data)
+}
+
+func Pretreatment(_id string, m map[string]interface{}) (bool, string, int, map[string]interface{}) {
+	if m == nil || len(m) == 0 {
+		log.Println(_id, "没有找到标讯")
+		return false, "", 0, nil
+	}
+	detail, _ := m["detail"].(string)
+	subtype, _ := m["subtype"].(string)
+	detail = strings.TrimSpace(strings.Replace(detail, "<br/>", "\n", -1))
+	for _, v := range clearPatterns {
+		detail = regexp.MustCompile(v).ReplaceAllString(detail, "")
+	}
+	bi := &BidInfo{
+		Id:          _id,
+		Detail:      detail,
+		Subtype:     subtype,
+		WinnerOrder: gconv.Strings(m["winnerorder"]),
+		Bidamount:   gconv.Float64(m["bidamount"]),
+	}
+	extract, _ := Mgo_Extract.FindById("result_20220219", _id, `{"kvtext":1}`)
+	if extract == nil || len(*extract) == 0 {
+		extract, _ = Mgo_Extract.FindById("result_20220218", _id, `{"kvtext":1}`)
+	}
+	if extract != nil && len(*extract) > 0 {
+		bi.KvText, _ = (*extract)["kvtext"].(string)
+	}
+	return service.Execute(bi)
+}