Compare commits

..

10 Commits

Author SHA1 Message Date
fallen-angle 9e3638885d finish 2022-05-16 19:55:59 +08:00
fallen-angle 7598280fc1 feat: management && notify 2022-05-04 20:06:21 +08:00
fallen-angle e58bafd0d3 Merge branch 'master' of https://mygitea.fallen-angle.com/fallen-angle/nCovTrack-Backend 2022-04-30 11:32:04 +08:00
fallen-angle 2482141d0f comm: swagger doc 2022-04-30 11:30:55 +08:00
Zhaolong 28c65b73c3 fix: 'province' & AreaInfo add parent 2022-04-28 00:19:30 +08:00
Zhaolong 09b8e8e262 fix: statistic upstream url change & save article to private 2022-04-27 23:12:20 +08:00
fallen-angle b92598ecc4 Merge branch 'master' of https://mygitea.fallen-angle.com/fallen-angle/nCovTrack-Backend
role merge
2022-04-27 22:08:30 +08:00
fallen-angle 22cb5ec61f fix: add role limit 2022-04-27 22:08:02 +08:00
fallen-angle 64a820a6df fix: add role limit 2022-04-27 19:12:51 +08:00
fallen-angle fc347a4140 feat: temp upload 2022-04-13 19:48:10 +08:00
53 changed files with 5673 additions and 322 deletions
+1393 -41
View File
File diff suppressed because it is too large Load Diff
+1387 -35
View File
File diff suppressed because it is too large Load Diff
+928 -30
View File
File diff suppressed because it is too large Load Diff
+15 -1
View File
@@ -1,4 +1,4 @@
package config
package global
type ServerConfig struct {
Listen string `yaml:"listen"`
@@ -11,6 +11,8 @@ type ServerConfig struct {
Redis RedisConfig `yaml:"redis"`
Jwt JwtConfig `yaml:"jwt"`
Email EmailConfig `yaml:"email"`
Bos BosConfig `yaml:"bos"`
Kafka KafkaCofig `yaml:"kafka"`
}
type MySQLConfig struct {
@@ -39,3 +41,15 @@ type EmailConfig struct {
Account string `yaml:"account"`
Password string `yaml:"password"`
}
type BosConfig struct {
AccessKey string `yaml:"accessKey"`
SecretKey string `yaml:"secretKey"`
Domain string `yaml:"domain"`
}
type KafkaCofig struct {
Servers []string `yaml:"servers"`
Topic string `yaml:"topic"`
Partition int `yaml:"partition"`
}
+41 -5
View File
@@ -3,22 +3,22 @@ package global
import (
"errors"
"fmt"
"nCovTrack-Backend/config"
"net/http"
"github.com/baidubce/bce-sdk-go/services/bos"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis"
"go.uber.org/zap"
"gorm.io/gorm"
"net/http"
)
var (
ServerSettings config.ServerConfig
ServerSettings ServerConfig
Db *gorm.DB
RootRouter *gin.RouterGroup
Logger *zap.SugaredLogger
HttpClient map[string]*http.Client
Redis *redis.Client
BosClient *bos.Client
)
func GetListenOn() string {
@@ -34,10 +34,46 @@ func GetHttpClient(key string) (*http.Client, error) {
}
const (
CHINA_NCOV_STATISTIC_URL = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5"
CHINA_NCOV_STATISTIC_URL = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=statisGradeCityDetail,diseaseh5Shelf"
CHINA_NCOV_STATISTIC_TREND_URL = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList"
ENV_NOLOG = "nolog"
TOKEN_EXPIRE_DAYS = 15
FACKER_HOST = "http://myhost.fallen-angle.com:5000/"
REGISTER_REDIS_KEY = "register_key"
CHANGEPWD_REDIS_KEY = "changepwd_key"
)
var (
ID_ROLE_MAP = map[int]string{
0: "SYSTEM",
4: "VOLUNTEER",
8: "WORKER",
12: "ADMIN",
}
ROLE_ID_MAP = map[string]int{
"SYSTEM": 0,
"VOLUNTEER": 4,
"WORKER": 8,
"ADMIN": 12,
}
HEALTH_SITUATION_ID_MAP = map[string]int{
"OTHER": 0,
"PATIENT": 1,
"CONTACT": 2,
"SUB_CONTACT": 3,
}
MEASURE_SITUATION_ID_MAP = map[string]int{
"NO_MEASURE": 0,
"NO_RISK": 1,
"TREATING": 2,
"CENTRALIZED": 3,
"HOME": 4,
}
PCR_RESULT_ID_MAP = map[string]int{
"NONE": 0,
"NEGATIVE": 1,
"POSITIVE": 2,
}
KafkaProducerChan = make(chan []byte)
)
+18 -2
View File
@@ -18,6 +18,12 @@ require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/Shopify/sarama v1.32.0 // indirect
github.com/baidubce/bce-sdk-go v0.9.112 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/eapache/go-resiliency v1.2.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@@ -30,13 +36,21 @@ require (
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.2 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@@ -48,6 +62,8 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.18.0 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/spf13/afero v1.8.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -58,8 +74,8 @@ require (
github.com/ugorji/go/codec v1.2.6 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // indirect
+54
View File
@@ -44,8 +44,13 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/sarama v1.32.0 h1:P+RUjEaRU0GMMbYexGMDyrMkLhbbBVUVISDywi+IlFU=
github.com/Shopify/sarama v1.32.0/go.mod h1:+EmJJKZWVT/faR9RcOxJerP+LId4iWdQPBGLy1Y1Njs=
github.com/Shopify/toxiproxy/v2 v2.3.0/go.mod h1:KvQTtB6RjCJY4zqNJn7C7JDFgsG5uoHYDirfUfpIm0c=
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
github.com/baidubce/bce-sdk-go v0.9.112 h1:qHzFxG7fwGbXCv+1smcbWFhWl6iwoXDVzPn9TUtrlss=
github.com/baidubce/bce-sdk-go v0.9.112/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -61,6 +66,12 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
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/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -69,6 +80,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
@@ -142,6 +155,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -155,6 +170,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -178,6 +194,12 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -185,6 +207,17 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@@ -201,6 +234,9 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw=
github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -258,6 +294,8 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -266,6 +304,8 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -274,6 +314,7 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60=
@@ -311,6 +352,9 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.0/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -338,6 +382,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -345,6 +390,9 @@ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+Wr
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -417,10 +465,13 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba h1:6u6sik+bn/y7vILcYkK3iwTBWN7WtBvB0+SZswQnbf8=
golang.org/x/net v0.0.0-20220121210141-e204ce36a2ba/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -454,6 +505,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -496,12 +548,14 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+53 -12
View File
@@ -1,11 +1,13 @@
package handler
import (
"github.com/gin-gonic/gin"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/service/article"
"nCovTrack-Backend/utils"
"strconv"
"github.com/gin-gonic/gin"
)
//SaveArticleHandler save an article
@@ -19,29 +21,60 @@ import (
// @Param Token header string true "token"
func SaveArticleHandler(c *gin.Context) {
jsonMap := bindJson(c)
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
if jsonMap == nil {
RequestErr(c, map[string]interface{}{"URI": c.Request.RequestURI})
return
}
colMap := models.MapJ2c[models.BackArticle](jsonMap, true)
if ok := article.SaveArticle(colMap); !ok {
if ok := article.SaveArticle(claims, colMap); !ok {
ServerErr(c, "Save Failed")
return
}
utils.Succ(c, jsonMap)
}
// GetAllArticlesHandler get all article
//ListPublishedArticlesHandler get all article
// @Tags Article
// @Accept json
// @Produce json
// @Summary get all articles
// @Description Admin can get not published article
// @Success 200 {object} utils.GinResponse{data=[]models.BackArticle}
// @Success 200 {object} utils.GinResponse{data=[]models.ListArticle}
// @Router /article/list [get]
func ListPublishedArticlesHandler(c *gin.Context) {
articles := article.ListPublishedArticles()
utils.Succ(c, articles)
}
//ListPublishedArticlesHandler get the user's article
// @Tags Article
// @Accept json
// @Produce json
// @Summary get user's articles
// @Success 200 {object} utils.GinResponse{data=[]models.BackArticle}
// @Router /article/list/{published} [get]
// @Param Token header string false "token"
func GetAllArticlesHandler(c *gin.Context) {
// TODO: admin need to show more articles
articles := article.ListAllArticles()
// @Param published path string true "string enums" Enums(published, notpublished)
func ListArticlesByUser(c *gin.Context) {
published := c.Param("published")
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
var articles *[]models.ListArticle
if published == "published" {
articles = article.ListPublishedArticlesByUser(claims.ID)
} else if published == "notpublished" {
articles = article.ListNotPublishedArticlesByUser(claims.ID)
} else {
UrlNotFound(c)
return
}
utils.Succ(c, articles)
}
@@ -56,6 +89,11 @@ func GetAllArticlesHandler(c *gin.Context) {
// @Param id path string true "id"
func DeleteArticleHandler(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
if err != nil {
RequestErr(c, map[string]interface{}{"URI": c.Request.RequestURI})
return
@@ -71,11 +109,10 @@ func DeleteArticleHandler(c *gin.Context) {
// @Tags Article
// @Accept json
// @Produce json
// @Summary get all articles
// @Summary get an articles
// @Description Admin can get not published article
// @Success 200 {object} utils.GinResponse{data=models.BackArticle}
// @Router /article/{id} [get]
// @Param Token header string false "token"
// @Param id path string true "id"
func GetArticleHandler(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
@@ -84,7 +121,6 @@ func GetArticleHandler(c *gin.Context) {
return
}
res := article.GetArticleById(id)
//TODO: if not admin, will not show not published article
if res == nil {
DataNotFound(c, nil)
return
@@ -96,13 +132,18 @@ func GetArticleHandler(c *gin.Context) {
// @Tags Article
// @Accept json
// @Produce json
// @Summary get all articles
// @Summary publish an articles
// @Success 200 {object} utils.GinResponse{}
// @Router /article/{id}/publish [post]
// @Param Token header string true "token"
// @Param id path string true "id"
func PublishArticleHandler(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
if err != nil {
RequestErr(c, map[string]interface{}{"URI": c.Request.RequestURI})
return
+15
View File
@@ -12,6 +12,10 @@ const (
BAD_REQUEST = "Bad Request"
DATA_NOT_FOUND = "Data not Found"
STATUS_DATA_NOT_FOUND = 210
FORBIDDENT = "FORBIDDENT"
PAGE_NOT_FOUND = "404 page not found"
STATUS_OPERATION_FAILED = 410
OPERATION_FAILED = "operation failed"
)
func RequestError(c *gin.Context, code int, data interface{}) {
@@ -31,3 +35,14 @@ func ServerErr(c *gin.Context, msg interface{}) {
func DataNotFound(c *gin.Context, data interface{}) {
utils.Success(c, http.StatusOK, STATUS_DATA_NOT_FOUND, DATA_NOT_FOUND, data)
}
func Forbidden(c *gin.Context) {
utils.Err(c, http.StatusForbidden, http.StatusForbidden, FORBIDDENT)
}
func UrlNotFound(c *gin.Context) {
c.String(http.StatusNotFound, PAGE_NOT_FOUND)
}
func OperationFailed(c *gin.Context) {
c.String(STATUS_OPERATION_FAILED, OPERATION_FAILED)
}
+234
View File
@@ -0,0 +1,234 @@
package handler
import (
"nCovTrack-Backend/global"
"nCovTrack-Backend/service/management"
"nCovTrack-Backend/utils"
"strconv"
"github.com/gin-gonic/gin"
)
//PullContactHandler pull contact from faker
// @Tags Management
// @Produce json
// @Summary pull contact from faker
// @Success 200 {object} utils.GinResponse
// @Router /management/faker/{patientId} [get]
// @Param Token header string true "Token"
// @Param patientId path int true "Patient Id"
func PullContactHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
patientId, _ := strconv.Atoi(c.Param("patientId"))
rowsAffected := management.PullFromFaker(claims, patientId)
if rowsAffected == -1 {
DataNotFound(c, map[string]interface{}{"patientId": patientId})
return
}
utils.Succ(c, map[string]interface{}{"pullAmount": rowsAffected})
}
//GetObservationHandler get observation's info
// @Tags Management
// @Produce json
// @Summary get observation's info
// @Success 200 {object} utils.GinResponse{data=models.BackObservation}
// @Router /management/observation/{id} [get]
// @Param token header string true "token"
// @Param id path int true "id"
func GetObservationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
id, _ := strconv.Atoi(c.Param("id"))
utils.Succ(c, management.GetObservation(id))
}
//ListObservationsHandler list observations by query
// @Tags Management
// @Produce json
// @Summary list observations by query
// @Success 200 {object} utils.GinResponse{data=[]models.ListObservation}
// @Router /management/observation/ [get]
// @Param Token header string true "token"
// @Param queries query models.BackObservation true "queries"
func ListObservationsHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
jsonMap := bindQuery(c)
utils.Succ(c, management.ListObservation(jsonMap))
}
//TreeifyObservationHandler get transform chain by user
// @Tags Management
// @Produce json
// @Summary get transform chain by user
// @Success 200 {object} utils.GinResponse{data=models.TreeObservation}
// @Router /management/observation/tree/{id}/{direction} [get]
// @Param Token header string true "token"
// @Param id path int true "id"
// @Param direction path string true "direction" Enums(up, down, all)
func TreeifyObservationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
id, _ := strconv.Atoi(c.Param("id"))
direction := c.Param("direction")
tree := management.TreeifyObservations(id, direction)
utils.Succ(c, tree)
}
//InsertObservationHandler insert observation
// @Tags Management
// @Accept json
// @Produce json
// @Summary insert observation
// @Success 200 {object} utils.GinResponse
// @Router /management/observation/ [post]
// @Param Observation body models.BackObservation true "observation"
// @Param Token header string true "token"
func InsertObservationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
observaion := bindJson(c)
delete(observaion, "id")
ok := management.InsertObservation(claims, observaion)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
//UpdateObservationHandler update observation
// @Tags Management
// @Accept json
// @Produce json
// @Summary update observation
// @Success 200 {object} utils.GinResponse
// @Router /management/observation/ [put]
// @Param Observation body models.BackObservation true "observation"
// @Param Token header string true "token"
func UpdateObservationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
observation := bindJson(c)
ok := management.UpdateObservation(claims, observation)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
//ListLocationHandler list locations
// @Tags Management
// @Produce json
// @Summary list locations
// @Success 200 {object} utils.GinResponse{data=models.BackLocation}
// @Router /management/location/ [get]
// @Param queries query models.BackLocation true "queries"
// @Param Token header string true "token"
func ListLocationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
queryMap := bindQuery(c)
utils.Succ(c, management.ListLocation(queryMap))
}
//InsertLocationHandler insert location
// @Tags Management
// @Accept json
// @Produce json
// @Summary insert location
// @Success 200 {object} utils.GinResponse
// @Router /management/location/ [post]
// @Param Location body models.BackLocation true "location"
// @Param Token header string true "token"
func InsertLocationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
locationMap := bindJson(c)
ok := management.InsertLocation(claims, locationMap)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
//DeleteLocationHandler delete location
// @Tags Management
// @Produce json
// @Summary delete location
// @Success 200 {object} utils.GinResponse
// @Router /management/location/{id} [delete]
// @Param Token header string true "token"
// @Param Id path int true "Id"
func DeleteLocationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
id, _ := strconv.Atoi(c.Param("id"))
ok := management.DeleteLocation(id)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
//InsertPcrHandler insert pcr record
// @Tags Management
// @Produce json
// @Summary insert pcr record
// @Success 200 {object} utils.GinResponse
// @Router /management/pcr/ [post]
// @Param Pcr body models.BackPcr true "Pcr"
// @Param Token header string true "token"
func InsertPcrHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
allowRows := []int{global.ROLE_ID_MAP["ADMIN"], global.ROLE_ID_MAP["WORKER"]}
if !utils.Contains(allowRows, claims.Role) {
Forbidden(c)
return
}
jsonMap := bindJson(c)
ok := management.InsertPcr(claims, jsonMap)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
+65
View File
@@ -0,0 +1,65 @@
package handler
import (
"nCovTrack-Backend/models"
"nCovTrack-Backend/service/notify"
"nCovTrack-Backend/utils"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
//InsertNotificationHandler for test
func InsertNotificationHandler(c *gin.Context) {
sendNotify := models.SendInfo{Region: []string{"江苏 徐州"}, Channel: []int{1, 0}, Notification: models.BackNotification{Time: time.Now(), Kind: "测试", Content: "Test"}}
notify.SendNotify(sendNotify)
}
//CountNotificationHandler count the notification
// @Tags Notification
// @Produce json
// @Summary count the notification
// @Success 200 {object} utils.GinResponse
// @Router /notify/count/ [get]
// @Param Token header string true "token"
func CountNotificationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
count := notify.QueryNotificationLen(claims.Region)
utils.Succ(c, map[string]interface{}{"count": count})
}
//ListNotificationHandler list the notifications in the range
// @Tags Notification
// @Produce json
// @Summary list the notification in the range
// @Success 200 {object} utils.GinResponse{data=[]models.BackNotification}
// @Router /notify/{start}/{end} [get]
// @Param Token header string true "token"
// @Param start path int true "start"
// @Param end path int true "end"
func ListNotificationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
start, _ := strconv.Atoi(c.Param("start"))
end, _ := strconv.Atoi(c.Param("end"))
notifications := notify.ListNotifycation(claims.Region, start, end)
utils.Succ(c, notifications)
}
//DeleteNotificationHandler delete the notify by index
// @Tags Notification
// @Produce json
// @Summary delete the notify by index, if -1 delete all
// @Success 200 {object} utils.GinResponse
// @Router /notify/{index} [delete]
// @Param Token header string true "token"
// @Param index path int true "index"
func DeleteNotificationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
index, _ := strconv.Atoi(c.Param("index"))
if index == -1 {
notify.CleanNotification(claims.Region)
}
notify.DeleteNotification(claims.Region, index)
utils.Succ(c, nil)
}
+12 -1
View File
@@ -17,7 +17,7 @@ import (
// @Param sort path string false "data sorted by" Enums(today, total, now, default)
func ProvinceDataHandler(c *gin.Context) {
sort := c.Param("sort")
data := service.GetAllProvienceData(sort)
data := service.GetAllProvinceData(sort)
utils.Succ(c, data)
}
@@ -57,3 +57,14 @@ func ChinaDataHandler(c *gin.Context) {
data := service.GetChinaNCovStatistic()
utils.Succ(c, data)
}
// ChinaTrendHandler Get china data
// @Tags Statistics
// @Produce json
// @Summary china trend
// @Success 200 {object} utils.GinResponse{data=models.ChinaTrend}
// @Router /statistics/china/trend [get]
func ChinaTrendHandler(c *gin.Context) {
data := service.GetChinaTrend()
utils.Succ(c, data)
}
+34 -9
View File
@@ -1,11 +1,13 @@
package handler
import (
"github.com/gin-gonic/gin"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/service/user"
"nCovTrack-Backend/utils"
"regexp"
"github.com/gin-gonic/gin"
)
//UserRegisterHandler user register
@@ -13,7 +15,7 @@ import (
// @Accept json
// @Produce json
// @Summary user register account
// @Success 200 {object} utils.GinResponse{}
// @Success 200 {object} utils.GinResponse{data=models.BackUser}
// @Router /user/register [post]
// @Param json body models.UserRegister true "json"
func UserRegisterHandler(c *gin.Context) {
@@ -22,11 +24,13 @@ func UserRegisterHandler(c *gin.Context) {
return
}
registered := user.NoDuplicatePhoneOrEmail(jsonMap["phone"].(string), jsonMap["email"].(string))
if registered {
if !registered {
utils.Success(c, 200, 200, "Registered", nil)
return
}
colMap := models.MapJ2c[models.BackUser](jsonMap, true)
user.Register(colMap)
utils.Succ(c, nil)
}
//UserApproveHandler admin approve account
@@ -39,12 +43,16 @@ func UserRegisterHandler(c *gin.Context) {
// @Param Token header string true "token"
// @Param json body models.UserApprove true "json"
func UserApproveHandler(c *gin.Context) {
//TODO: auth user is admin or not
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
jsonMap := bindJsonStruct[models.UserApprove](c)
if jsonMap == nil {
return
}
if !user.ApproveRegister(jsonMap["email"].(string), jsonMap["pass"].(bool)) {
if !user.ApproveRegister(claims, jsonMap["email"].(string), jsonMap["pass"].(bool)) {
RequestErr(c, "approve failed")
return
}
@@ -66,7 +74,9 @@ func UserLoginHandler(c *gin.Context) {
}
token := user.Login(jsonMap)
if token == "" {
// TODO: change to request error
// Login failed reasons as follow:
// 1. account or password incorrect
// 2. account apply not pass
utils.Succ(c, map[string]interface{}{"msg": "failed"})
return
}
@@ -79,10 +89,25 @@ func UserLoginHandler(c *gin.Context) {
// @Produce json
// @Summary list register infos, which is to be approved
// @Success 200 {object} utils.GinResponse{}
// @Router /user/registers [get]
// @Router /user/registers/{approved} [get]
// @Param Token header string true "token"
// @Param approved path string true "string enums" Enums(approved, notapproved)
func ListRegisterUserHandler(c *gin.Context) {
registers := user.ListRegister()
approved := c.Param("approved")
claims := utils.ClaimsFromHeader(c)
if claims.Role != global.ROLE_ID_MAP["ADMIN"] {
Forbidden(c)
return
}
var registers *[]map[string]interface{}
if approved == "notapproved" {
registers = user.ListRegister(claims)
} else if approved == "approved" {
registers = user.ListApprovedRegister(claims)
} else {
UrlNotFound(c)
return
}
utils.Succ(c, registers)
}
@@ -91,7 +116,7 @@ func ListRegisterUserHandler(c *gin.Context) {
// @Produce json
// @Summary send verify code
// @Success 200 {object} utils.GinResponse{}
// @Router /user/{code} [get]
// @Router /user/code/{email} [get]
// @Param email path string true "email"
func SendEmailCodeHandler(c *gin.Context) {
email := c.Param("email")
+10
View File
@@ -2,6 +2,7 @@ package handler
import (
"encoding/json"
"github.com/gin-gonic/gin"
)
@@ -46,3 +47,12 @@ func bindJsonStruct[T any](c *gin.Context) map[string]interface{} {
}
return jsonMap
}
func bindQuery(c *gin.Context) map[string]interface{} {
jsonMap := map[string]interface{}{}
queries := c.Request.URL.Query()
for k := range queries {
jsonMap[k] = c.Query(k)
}
return jsonMap
}
+20
View File
@@ -0,0 +1,20 @@
package initialize
import (
"github.com/baidubce/bce-sdk-go/services/bos"
"nCovTrack-Backend/global"
)
func initBos() {
clientConfig := bos.BosClientConfiguration{
global.ServerSettings.Bos.AccessKey,
global.ServerSettings.Bos.SecretKey,
global.ServerSettings.Bos.Domain,
false,
}
bosClient, err := bos.NewClientWithConfig(&clientConfig)
if err != nil {
panic(err)
}
global.BosClient = bosClient
}
+2 -4
View File
@@ -2,11 +2,9 @@ package initialize
import (
"fmt"
"nCovTrack-Backend/config"
"nCovTrack-Backend/global"
"github.com/fatih/color"
"github.com/spf13/viper"
"nCovTrack-Backend/global"
)
func initConfig() {
@@ -16,7 +14,7 @@ func initConfig() {
if err := v.ReadInConfig(); err != nil {
panic(err)
}
serverConfig := config.ServerConfig{}
serverConfig := global.ServerConfig{}
if err := v.Unmarshal(&serverConfig); err != nil {
panic(err)
}
+1
View File
@@ -10,5 +10,6 @@ func initCron() {
c := cron.New()
//c.AddFunc("@every 10s", func() { global.Redis.Set("OK", time.Now().String(), time.Duration(10*time.Hour)) })
c.AddFunc("@every 10m", statistics.CacheNCov)
c.AddFunc("@every 8h", statistics.CacheNCovTrend)
c.Start()
}
+3
View File
@@ -8,7 +8,10 @@ func Initialize() *gin.Engine {
initHttpClient()
initMySQL()
initRedis()
initBos()
initCron()
go initProducer()
go initConsumer()
g := initRouter()
initSwagger()
return g
+70
View File
@@ -0,0 +1,70 @@
package initialize
import (
"nCovTrack-Backend/global"
"nCovTrack-Backend/service/notify"
"time"
"github.com/Shopify/sarama"
)
func initProducer() {
config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForAll
config.Producer.Partitioner = sarama.NewRandomPartitioner
config.Producer.Return.Successes = true
config.Producer.Return.Errors = true
producer, err := sarama.NewSyncProducer(global.ServerSettings.Kafka.Servers, config)
if err != nil {
panic("kafka init failed")
}
// defer producer.Close()
for {
select {
case value := <-global.KafkaProducerChan:
producer.SendMessage(&sarama.ProducerMessage{Topic: global.ServerSettings.Kafka.Topic, Key: nil, Value: sarama.ByteEncoder(value)})
}
}
}
func initConsumer() {
config := sarama.NewConfig()
config.Consumer.Offsets.CommitInterval = 1 * time.Second
client, err := sarama.NewClient(global.ServerSettings.Kafka.Servers, config)
if err != nil {
panic("kafka init failed")
}
defer client.Close()
offsetManager, err := sarama.NewOffsetManagerFromClient("", client)
if err != nil {
panic("kafka init failed")
}
defer offsetManager.Close()
partitionOffsetManager, err := offsetManager.ManagePartition(global.ServerSettings.Kafka.Topic, int32(global.ServerSettings.Kafka.Partition))
if err != nil {
panic("kafka init failed")
}
defer partitionOffsetManager.Close()
offsetNewest, _ := partitionOffsetManager.NextOffset()
consumer, err := sarama.NewConsumer(global.ServerSettings.Kafka.Servers, config)
if err != nil {
panic("kafka init failed")
}
defer consumer.Close()
partitionConsumer, err := consumer.ConsumePartition(global.ServerSettings.Kafka.Topic, int32(global.ServerSettings.Kafka.Partition), offsetNewest)
if err != nil {
panic("kafka init failed")
}
defer partitionConsumer.Close()
for {
select {
case msg := <-partitionConsumer.Messages():
ConsumeFunc(msg.Value)
}
}
}
func ConsumeFunc(msg []byte) {
notify.HandleKafkaNotify(msg)
}
+1 -1
View File
@@ -9,6 +9,6 @@ import (
)
func initSwagger() {
docs.SwaggerInfo_swagger.BasePath = "/api" + global.ServerSettings.UrlPrefix
docs.SwaggerInfo.BasePath = "/api" + global.ServerSettings.UrlPrefix
global.RootRouter.GET("/swagger/*any", swagger.WrapHandler(swaggerFile.Handler))
}
+9
View File
@@ -3,11 +3,20 @@ package main
import (
"nCovTrack-Backend/global"
"nCovTrack-Backend/initialize"
"os"
"os/signal"
"syscall"
)
// @title nCov Tracker
// @version 1.0
func main() {
signals := make(chan os.Signal)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
go func() {
<-signals
os.Exit(1)
}()
gin := initialize.Initialize()
gin.Run(global.GetListenOn())
}
+14 -6
View File
@@ -1,14 +1,16 @@
package middleware
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
"encoding/json"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
const UNAUTH_MSG = "unauthorized"
@@ -24,9 +26,15 @@ func Auth() gin.HandlerFunc {
}
// Write the field of token to request header
claims := utils.ParseClaims(oldToken[0])
c.Request.Header.Set("role", fmt.Sprint(claims["role"]))
c.Request.Header.Set("email", claims["email"].(string))
c.Request.Header.Set("id", fmt.Sprint(claims["id"]))
tokenClaims := models.TokenClaims{
ID: int(claims["id"].(float64)),
Username: claims["username"].(string),
Email: claims["email"].(string),
Role: int(claims["role"].(float64)),
Region: claims["region"].(string),
}
claimsByte, _ := json.Marshal(tokenClaims)
c.Request.Header.Add("claims", string(claimsByte))
// renew token, and judge the token's iat is expired or not
renewToken := utils.RenewToken(oldToken[0])
+1 -1
View File
@@ -13,7 +13,7 @@ func Cors() gin.HandlerFunc {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, X-Token, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
+14 -13
View File
@@ -6,11 +6,11 @@ import (
// BackArticle article struct
type BackArticle struct {
ID int `gorm:"primaryKey;column:id" json:"-"` // 文章id
ID int `gorm:"primaryKey;column:id" json:"id"` // 文章id
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 文章新建时间
CreateUser string `gorm:"column:create_user" json:"createUser"` // 文章创建者id
CreateUser int `gorm:"column:create_user" json:"createUser"` // 文章创建者id
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 文章最后更新时间
ModifyUser string `gorm:"column:modify_user" json:"modifyUser"` // 文章最后更新者id
ModifyUser int `gorm:"column:modify_user" json:"modifyUser"` // 文章最后更新者id
Title string `gorm:"column:title" json:"title"` // 文章标题
Tags string `gorm:"column:tags" json:"tags"` // 文章Tag
Resume string `gorm:"column:resume" json:"resume"` // 文章简述
@@ -20,16 +20,17 @@ type BackArticle struct {
IsDelete int8 `gorm:"column:is_delete" json:"isDelete"` // 删除标志
}
type ListArticle struct {
ID int `json:"id"`
Username string `json:"username"`
CreateTime time.Time `json:"createTime"`
ModifyTime time.Time `json:"modifyTime"`
Title string `json:"title"`
Tags string `json:"tags"`
Resume string `json:"resume"`
Cover string `json:"cover"`
}
func init() {
initJcMap[BackArticle]()
}
//func ArticleMapJ2c(jsonMap map[string]interface{}, ignoreNil bool) map[string]interface{} {
// colMap := make(map[string]interface{})
// for k, v := range jsonMap {
// if colKey := colKey != "" && (!ignoreNil && v == nil) {
// colMap[colKey] = v
// }
// }
// return colMap
//}
+1
View File
@@ -0,0 +1 @@
package models
+177
View File
@@ -0,0 +1,177 @@
package models
import (
"fmt"
"time"
)
type BackObservation struct {
ID int `gorm:"primaryKey;column:id" json:"id"` // ID
Name string `gorm:"column:name" json:"name"` // 姓名
Age int `gorm:"column:age" json:"age"` // 年龄
Sex int `gorm:"column:sex" json:"sex"` // 性别
Phone string `gorm:"column:phone" json:"phone"` // 手机号码
Identification string `gorm:"column:identification" json:"identification"` // 身份证号
ContactPerson int `gorm:"column:contact_person" json:"contactPerson"` // 接触者id
Region string `gorm:"column:region" json:"region"` // 受观察者所在区域
Address string `gorm:"column:address" json:"address"` // 受观察者所在具体地点
HealthSituation int `gorm:"column:health_situation" json:"healthSituation"` // 被观察者的疫情状况:0- 其他,1-患者,2-密接,3-次密接
HealthChangeTime time.Time `gorm:"column:health_change_time" json:"healthChangeTime"` // 患者健康状况转化时间
MeasureSituation int `gorm:"column:measure_situation" json:"measureSituation"` // 受观察者被采取措施状况 :0-未采取措施,1-解除风险,2-正在治疗,3-集中隔离,4-居家隔离
Trajectory string `gorm:"column:trajectory" json:"trajectory"` // 行程轨迹
CreateUser int `gorm:"column:create_user" json:"createUser"` // 创建者
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 创建时间
ModifyUser int `gorm:"column:modify_user" json:"modifyUser"` // 修改者
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 修改时间
IsDelete int `gorm:"column:is_delete" json:"isDelete"` // 删除标志
}
type BackLocation struct {
ID int `gorm:"primaryKey;column:id" json:"id"` // ID
Name string `gorm:"column:name" json:"name"` // 地点名
Region string `gorm:"column:region" json:"region"` // 地点所在地区
Address string `gorm:"column:address" json:"address"` // 地点的精确地址
PrincipalName string `gorm:"column:principal_name" json:"principalName"` // 负责人姓名
PrincipalPhone string `gorm:"column:principal_phone" json:"principalPhone"` // 负责人电话
CreateUser int `gorm:"column:create_user" json:"createUser"` // 创建者
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 创建时间
ModifyUser int `gorm:"column:modify_user" json:"modifyUser"` // 修改者
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 修改时间
IsDelete int `gorm:"column:is_delete" json:"isDelete"` // 删除标志
}
type BackPcr struct {
ID int `gorm:"primaryKey;column:id" json:"id"` // ID
Observation int `gorm:"column:observation" json:"observation"` // 观察对象ID
DetectTime time.Time `gorm:"column:detect_time" json:"detectTime"` // 核酸检测时间
DetectResult int `gorm:"column:detect_result" json:"detectResult"` // 核酸检测结果:0-未检测,1-阴性,2-阳性
CreateUser int `gorm:"column:create_user" json:"createUser"` // 创建者
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 创建时间
ModifyUser int `gorm:"column:modify_user" json:"modifyUser"` // 修改者
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 修改时间
IsDelete int `gorm:"column:is_delete" json:"isDelete"` // 删除标志
}
type BackSituationRecord struct {
ID int `gorm:"primaryKey;column:id" json:"id"` // ID
Observation int `gorm:"column:observation" json:"observation"` // 观察对象
Record string `gorm:"column:record" json:"record"` // 状态转化记录
CreateUser int `gorm:"column:create_user" json:"createUser"` // 创建者
CreateTime time.Time `gorm:"column:create_time" json:"createTime"` // 创建时间
ModifyUser int `gorm:"column:modify_user" json:"modifyUser"` // 修改者
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"` // 修改时间
IsDelete int `gorm:"column:is_delete" json:"isDelete"` // 删除标志
}
type ListObservation struct {
ID int `json:"id"`
Name string `json:"name"` // 姓名
Age int `json:"age"` // 年龄
Sex int `json:"sex"` // 性别
Phone string `json:"phone"` // 手机号码
Identification string `json:"identification"` // 身份证号
ContactPerson int `json:"contactPerson"` // 接触者id
Region string `json:"region"` // 受观察者所在区域
Address string `json:"address"` // 受观察者所在具体地点
HealthSituation int `json:"healthSituation"` // 被观察者的疫情状况:0- 其他,1-患者,2-密接,3-次密接
HealthChangeTime time.Time `json:"healthChangeTime"` // 患者健康状况转化时间
MeasureSituation int `json:"measureSituation"` // 受观察者被采取措施状况 :0-未采取措施,1-解除风险,2-正在治疗,3-集中隔离,4-居家隔离
Trajectory string `json:"trajectory"` // 行程轨迹
CreateUser int `json:"createUser"` // 创建者
CreateTime time.Time `json:"createTime"` // 创建时间
ModifyUser int `json:"modifyUser"` // 修改者
ModifyTime time.Time `json:"modifyTime"` // 修改时间
PcrTime time.Time `json:"pcrTime"` // 核酸时间
PcrResult int `json:"pcrResult"` // 核酸结果:0-未检测,1-阴性,2-阳性
RecordTime time.Time `json:"recordTime"` // 状态转换时间
Record string `json:"record"` // 状态转换内容
}
type TreeObservation struct {
ID int `json:"id"`
Name string `json:"name"` // 姓名
Age int `json:"age"` // 年龄
Sex int `json:"sex"` // 性别
Phone string `json:"phone"` // 手机号码
Identification string `json:"identification"` // 身份证号
ContactPerson int `json:"contactPerson"` // 接触者id
Region string `json:"region"` // 受观察者所在区域
Address string `json:"address"` // 受观察者所在具体地点
HealthSituation int `json:"healthSituation"` // 被观察者的疫情状况:0- 其他,1-患者,2-密接,3-次密接
HealthChangeTime time.Time `json:"healthChangeTime"` // 患者健康状况转化时间
MeasureSituation int `json:"measureSituation"` // 受观察者被采取措施状况 :0-未采取措施,1-解除风险,2-正在治疗,3-集中隔离,4-居家隔离
Trajectory string `json:"trajectory"` // 行程轨迹
CreateUser int `json:"createUser"` // 创建者
CreateTime time.Time `json:"createTime"` // 创建时间
ModifyUser int `json:"modifyUser"` // 修改者
ModifyTime time.Time `json:"modifyTime"` // 修改时间
Children []*TreeObservation `json:"children"` // 子结点
}
type QueryObservation struct {
BackObservation
PcrRecord []BackPcr `json:"pcrRecord"`
SituationRecord []BackSituationRecord `json:"situationRecord"`
}
func init() {
initJcMap[BackObservation]()
initJcMap[BackLocation]()
initJcMap[BackPcr]()
initJcMap[BackSituationRecord]()
}
type FakerDate time.Time
const (
timeFormat = "2006-01-02"
)
func (t *FakerDate) UnmarshalJSON(data []byte) (err error) {
newTime, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
*t = FakerDate(newTime)
return
}
func (t FakerDate) MarshalJSON() ([]byte, error) {
timeStr := fmt.Sprintf("\"%s\"", time.Time(t).Format(timeFormat))
return []byte(timeStr), nil
}
func (t FakerDate) String() string {
return time.Time(t).Format(timeFormat)
}
type HotelContactRequest struct {
Name string `json:"name"`
Age int `json:"age,string"`
Sex int `json:"sex,string"`
Phone string `json:"phone"`
Address string `json:"address"`
HotelCode string `json:"hotel_code"`
HotelName string `json:"hotel_name"`
LocateCityId string `json:"locate_city_id"`
Identification string `json:"identification"`
InData FakerDate `json:"in_data"`
OutData FakerDate `json:"out_data"`
}
type RailwayContactRequest struct {
Name string `json:"name"`
Age int `json:"age,string"`
Sex int `json:"sex,string"`
Phone string `json:"phone"`
Address string `json:"address"`
Train string `json:"train"`
Launch FakerDate `json:"launch"`
Identification string `json:"identification"`
}
type PatientRequest struct {
Name string `json:"name"`
Age int `json:"age,string"`
Sex int `json:"sex,string"`
Phone string `json:"phone"`
Address string `json:"address"`
Identification string `json:"identification"`
}
+15
View File
@@ -0,0 +1,15 @@
package models
import "time"
type BackNotification struct {
Time time.Time `json:"time"`
Kind string `json:"kind"`
Content string `json:"content"`
}
type SendInfo struct {
Region []string `json:"region"`
Channel []int `json:"channel"`
Notification BackNotification `json:"notification"`
}
+40
View File
@@ -2,6 +2,7 @@ package models
type AreaInfo struct {
Name string `json:"name"`
Parent string `json:"parent"`
Today AreaToday `json:"today"`
Total AreaTotal `json:"total"`
Children []AreaInfo `json:"children"`
@@ -58,3 +59,42 @@ type ChinaData struct {
ChinaAdd ChinaAdd `json:"chinaAdd"`
ChinaTotal ChinaTotal `json:"chinaTotal"`
}
type ChinaDayAdd struct {
DeadRate string `json:"deadRate"`
HealRate string `json:"healRate"`
Date string `json:"date"`
Year string `json:"y"`
Confirm int `json:"confirm"`
Suspect int `json:"suspect"`
Dead int `json:"dead"`
Infect int `json:"infect"`
Heal int `json:"heal"`
ImportedCase int `json:"importedCase"`
Localinfectionadd int `json:"localinfectionadd"`
LocalConfirmadd int `json:"localConfirmadd"`
}
type ChinaDay struct {
DeadRate string `json:"deadRate"`
NoInfect int `json:"noInfect"`
LocalConfirm int `json:"localConfirm"`
ImportedCase int `json:"importedCase"`
Date string `json:"date"`
LocalConfirmH5 int `json:"localConfirmH5"`
Suspect int `json:"suspect"`
Dead int `json:"dead"`
Heal int `json:"heal"`
Year string `json:"y"`
Confirm int `json:"confirm"`
NowConfirm int `json:"nowConfirm"`
HealRate string `json:"healRate"`
NowSevere int `json:"nowSevere"`
NoInfectH5 int `json:"noInfectH5"`
LocalAccConfirm int `json:"local_acc_confirm"`
}
type ChinaTrend struct {
ChinaDayList []ChinaDay `json:"ChinaDayList"`
ChinaDayAddList []ChinaDayAdd `json:"chinaDayAddList"`
}
+12 -1
View File
@@ -3,7 +3,7 @@ package models
import "time"
type BackUser struct {
ID int `gorm:"primaryKey;column:id" json:"-"` // 用户ID
ID int `gorm:"primaryKey;column:id" json:"id"` // 用户ID
Username string `gorm:"column:username" json:"username"` // 用户真实姓名
Password string `gorm:"column:password" json:"password"` // 用户密码
Role int `gorm:"column:role" json:"role"` // 用户角色
@@ -14,6 +14,7 @@ type BackUser struct {
Approver int `gorm:"column:approver" json:"approver"` // 注册审核人ID
ModifyTime time.Time `gorm:"column:modify_time" json:"modifyTime"`
IsDelete int8 `gorm:"column:is_delete" json:"isDelete"` // 删除标志
Region string `gorm:"column:region" json:"region"` // 用户所属地域
}
type UserLogin struct {
@@ -27,6 +28,8 @@ type UserRegister struct {
Email string `json:"email"`
Phone string `json:"phone"`
Aptitude string `json:"aptitude"`
Region string `json:"region"`
Role int `json:"role"`
}
type UserChangePwd struct {
@@ -40,6 +43,14 @@ type UserApprove struct {
Pass bool `json:"pass"`
}
type TokenClaims struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Role int `json:"role"`
Region string `json:"region"`
}
func init() {
initJcMap[BackUser]()
}
+94 -4
View File
@@ -2,11 +2,13 @@ package models
import (
"fmt"
"gorm.io/gorm"
"nCovTrack-Backend/global"
"reflect"
"regexp"
"strconv"
"time"
"gorm.io/gorm"
)
var colNameReg, _ = regexp.Compile(".*column:(.*);?")
@@ -60,17 +62,60 @@ func MapJ2c[T any](jsonMap map[string]interface{}, ignoreNil bool) (colMap map[s
return colMap
}
func MapsJ2c[T any](jsonMaps []map[string]interface{}, ignoreNil bool) (colMaps []map[string]interface{}) {
for _, jsonMap := range jsonMaps {
colMap := MapJ2c[T](jsonMap, ignoreNil)
colMaps = append(colMaps, colMap)
}
return colMaps
}
func MapC2j[T any](colMap map[string]interface{}, ignoreNil bool) (jsonMap map[string]interface{}) {
tName := reflect.TypeOf(new(T)).Elem().Name()
tC2jMap := c2jMap[tName]
if tC2jMap == nil {
panic(tName + " is not init registered int j2cMap")
}
jsonMap = make(map[string]interface{})
for k, v := range colMap {
//TODO 无法转换
if jsonKey := tC2jMap[k]; jsonKey != "" && (!ignoreNil || v != nil) {
jsonMap[jsonKey] = v
}
}
return jsonMap
}
func MapsC2j[T any](jsonMaps []map[string]interface{}, ignoreNil bool) (colMaps []map[string]interface{}) {
for _, jsonMap := range jsonMaps {
colMap := MapC2j[T](jsonMap, ignoreNil)
colMaps = append(colMaps, colMap)
}
return colMaps
}
// BeforeSave need to set some field while insert or update
func BeforeSave(colMap map[string]interface{}, user int) {
if colMap["id"] == nil {
if colMap["id"] == nil || int(colMap["id"].(float64)) == 0 {
colMap["create_time"] = time.Now()
if user != -1 {
colMap["create_user"] = user
} else {
colMap["create_user"] = 0
}
}
colMap["modify_time"] = time.Now()
if user != -1 {
colMap["modify_user"] = user
} else {
colMap["modify_user"] = 0
}
}
func BeforeBatchSave(colMaps *[]map[string]interface{}, user int) {
for _, colMap := range *colMaps {
BeforeSave(colMap, user)
}
}
@@ -81,7 +126,7 @@ func BeforeSave(colMap map[string]interface{}, user int) {
func Upsert[T any](colMap map[string]interface{}) (ok bool, rowsAffected int64) {
var tx *gorm.DB
if colMap["id"] == nil {
if colMap["id"] == nil || getMapId(colMap["id"]) == 0 {
tx = global.Db.Model(new(T)).Create(colMap)
} else {
tx = global.Db.Model(new(T)).Where("id = ?", colMap["id"]).Updates(colMap)
@@ -93,13 +138,44 @@ func Upsert[T any](colMap map[string]interface{}) (ok bool, rowsAffected int64)
return true, tx.RowsAffected
}
func BatchInsert[T any](colMaps []map[string]interface{}) (ok bool, rowsAffected int64) {
tx := global.Db.Model(new(T)).Create(&colMaps)
if tx.Error != nil {
fmt.Println(tx.Error)
return false, 0
}
return true, tx.RowsAffected
}
func Update[T any](queryMap []map[string]interface{}, updateMap map[string]interface{}) (ok bool, rowsAffected int64) {
tx := global.Db.Model(new(T))
for _, e := range queryMap {
e[IS_DELETE] = 0
tx = tx.Or(e)
}
return UpdateByOrm(tx, updateMap)
}
func UpdateByOrm(tx *gorm.DB, updateMap map[string]interface{}) (ok bool, rowsAffected int64) {
tx.Updates(updateMap)
if tx.Error != nil {
fmt.Println(tx.Error)
return false, 0
}
return true, tx.RowsAffected
}
// DeleteById will delete by id, not delete the record from database, only set the field "is_delete" as 1
func DeleteById[T any](id int) (ok bool, rowsAffected int64) {
tx := global.Db.Model(new(T)).Where("id = ?", id).Update("is_delete", 1)
if tx.Error != nil {
return false, 0
}
return true, rowsAffected
return true, tx.RowsAffected
}
func DropById[T any](id int) {
global.Db.Model(new(T)).Delete("id = ?", id)
}
func List[T any](queryMap []map[string]interface{}) *[]map[string]interface{} {
@@ -179,3 +255,17 @@ func CountByOrm(tx *gorm.DB) int64 {
tx.Count(&count)
return count
}
func getMapId(id interface{}) int {
switch id.(type) {
case int:
return id.(int)
case string:
id, _ := strconv.Atoi(id.(string))
return id
case float64:
return int(id.(float64))
default:
return -1
}
}
+3 -2
View File
@@ -8,16 +8,17 @@ import (
func articlePrivateRouter(router *gin.RouterGroup) {
articleRouter := router.Group("/article")
{
articleRouter.POST("", handler.SaveArticleHandler)
articleRouter.DELETE("/:id", handler.DeleteArticleHandler)
articleRouter.POST("/:id/publish", handler.PublishArticleHandler)
articleRouter.GET("/list/:published", handler.ListArticlesByUser)
}
}
func articlePublicRouter(router *gin.RouterGroup) {
articleRouter := router.Group("/article")
{
articleRouter.POST("", handler.SaveArticleHandler)
articleRouter.GET("/list", handler.GetAllArticlesHandler)
articleRouter.GET("/list", handler.ListPublishedArticlesHandler)
articleRouter.GET("/:id", handler.GetArticleHandler)
}
}
+23
View File
@@ -0,0 +1,23 @@
package router
import (
"nCovTrack-Backend/handler"
"github.com/gin-gonic/gin"
)
func managementPrivateRouter(router *gin.RouterGroup) {
managementRouter := router.Group("/management")
{
managementRouter.GET("/faker/:patientId", handler.PullContactHandler)
managementRouter.GET("/observation", handler.ListObservationsHandler)
managementRouter.POST("/observation", handler.InsertObservationHandler)
managementRouter.GET("/observation/:id", handler.GetObservationHandler)
managementRouter.GET("/observation/tree/:id/:direction", handler.TreeifyObservationHandler)
managementRouter.PUT("/observation", handler.UpdateObservationHandler)
managementRouter.POST("/location/", handler.InsertLocationHandler)
managementRouter.GET("/location/", handler.ListLocationHandler)
managementRouter.DELETE("/location/", handler.DeleteLocationHandler)
managementRouter.POST("/pcr/", handler.InsertPcrHandler)
}
}
+17
View File
@@ -0,0 +1,17 @@
package router
import (
"nCovTrack-Backend/handler"
"github.com/gin-gonic/gin"
)
func notifyPrivateRouter(router *gin.RouterGroup) {
notifyRouter := router.Group("/notify")
{
notifyRouter.GET("/count", handler.CountNotificationHandler)
notifyRouter.POST("/", handler.InsertNotificationHandler)
notifyRouter.GET("/:start/:end", handler.ListNotificationHandler)
notifyRouter.DELETE("/:index", handler.DeleteNotificationHandler)
}
}
+2
View File
@@ -21,5 +21,7 @@ func BusiRouter() {
{
articlePrivateRouter(privateRouter)
userPrivateRouter(privateRouter)
notifyPrivateRouter(privateRouter)
managementPrivateRouter(privateRouter)
}
}
+2 -1
View File
@@ -8,10 +8,11 @@ import (
func statisticRouter(router *gin.RouterGroup) {
statisticsRouter := router.Group("/statistics")
{
statisticsRouter.GET("/provience/:sort", handler.ProvinceDataHandler)
statisticsRouter.GET("/province/:sort", handler.ProvinceDataHandler)
statisticsRouter.GET("/city/:sort", handler.CityDataHandler)
statisticsRouter.GET("/country/child", handler.CountryDataHandler)
statisticsRouter.GET("/country", handler.CountryDataHandler)
statisticsRouter.GET("/china", handler.ChinaDataHandler)
statisticsRouter.GET("/china/trend", handler.ChinaTrendHandler)
}
}
+1 -1
View File
@@ -19,6 +19,6 @@ func userPrivateRouter(router *gin.RouterGroup) {
userRouter := router.Group("/user")
{
userRouter.POST("/approve", handler.UserApproveHandler)
userRouter.GET("/registers", handler.ListRegisterUserHandler)
userRouter.GET("/registers/:approved", handler.ListRegisterUserHandler)
}
}
+30 -15
View File
@@ -1,31 +1,46 @@
package article
import (
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"strconv"
)
//ListPublishedArticles list the articles published, use to show the articles to all people
func ListPublishedArticles() *[]map[string]interface{} {
article := models.ListField[models.BackArticle]([]map[string]interface{}{{"is_publish": 0}}, true, "content")
if *article == nil {
article = &[]map[string]interface{}{}
}
return article
func ListPublishedArticles() *[]models.ListArticle {
return listArticles(1, 0)
}
//ListAllArticles list all articles, will show the articles not published of the user
// TODO: need only show the user's not published article
func ListAllArticles() *[]map[string]interface{} {
article := models.ListField[models.BackArticle]([]map[string]interface{}{{}}, true, "content")
if *article == nil {
article = &[]map[string]interface{}{}
//ListPublishedArticlesByUser list the user's published articles
func ListPublishedArticlesByUser(id int) *[]models.ListArticle {
return listArticles(1, id)
}
return article
//ListNotPublishedArticlesByUser list the user's not publish articles
func ListNotPublishedArticlesByUser(id int) *[]models.ListArticle {
return listArticles(0, id)
}
//listArticles complex function need to directly use gorm
func listArticles(isPublish int, createUser int) *[]models.ListArticle {
queryStr := "back_article.is_delete = 0 AND is_publish = " + strconv.Itoa(isPublish)
if createUser != 0 {
queryStr += " AND create_user = " + strconv.Itoa(createUser)
}
var res []models.ListArticle
global.Db.Table("back_article").
Select("back_user.username, back_article.*").
Joins("join back_user on back_article.create_user=back_user.id").
Where(queryStr).Find(&res)
if res == nil {
res = []models.ListArticle{}
}
return &res
}
//SaveArticle save the articles
func SaveArticle(article map[string]interface{}) (ok bool) {
models.BeforeSave(article, -1)
func SaveArticle(claims models.TokenClaims, article map[string]interface{}) (ok bool) {
models.BeforeSave(article, claims.ID)
ok, rows := models.Upsert[models.BackArticle](article)
return ok && rows != 0
}
+111
View File
@@ -0,0 +1,111 @@
package management
import (
"encoding/json"
"fmt"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
"strings"
"time"
)
//fakerGetRequest Get data from faker
func fakerGetRequest(uri string) string {
resStr := utils.GetWhioutHeader(global.FACKER_HOST + uri)
var res utils.GinResponse
_ = json.Unmarshal([]byte(resStr), &res)
dataStr, _ := json.Marshal(res.Data)
return string(dataStr)
}
//queryHotelContacts Hotel contacts
func queryHotelContacts(identification string) []models.HotelContactRequest {
dataStr := fakerGetRequest("query/contacts/hotel/" + identification)
var data []models.HotelContactRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}
//queryRailwayContacts Railway contacts
func queryRailwayContacts(identification string) []models.RailwayContactRequest {
dataStr := fakerGetRequest("query/contacts/railway/" + identification)
var data []models.RailwayContactRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}
//queryPatients Patients
func QueryPatients(identification string) []models.PatientRequest {
dataStr := fakerGetRequest("query/contacts/railway/" + identification)
var data []models.PatientRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}
//queryContacts Resolve the diffrent of hotel and railway request
func queryContacts(identification string) []models.BackObservation {
hotelContacts := queryHotelContacts(identification)
railwayContacts := queryRailwayContacts(identification)
observations := append(fakerContacts2Observations(hotelContacts), fakerContacts2Observations(railwayContacts)...)
return observations
}
//splitFakerAddress Extract the region
func splitFakerAddress(fakerAddress string) string {
addresses := strings.Split(fakerAddress, " ")
return addresses[1] + " " + addresses[2]
}
//fakerContacts2Observations Convvert structs
func fakerContacts2Observations(contacts any) []models.BackObservation {
var observations []models.BackObservation
switch contacts.(type) {
case []models.HotelContactRequest:
for _, contact := range contacts.([]models.HotelContactRequest) {
tranjectory := fmt.Sprintf("在%s-%s期间,与患者同期留宿%s", contact.InData.String(), contact.OutData.String(), contact.HotelName)
observation := &models.BackObservation{
Name: contact.Name,
Age: contact.Age,
Sex: contact.Sex,
Phone: contact.Phone,
Identification: contact.Identification,
Region: splitFakerAddress(contact.Address),
Address: "",
HealthSituation: global.HEALTH_SITUATION_ID_MAP["CONTACT"],
HealthChangeTime: time.Now(),
MeasureSituation: global.MEASURE_SITUATION_ID_MAP["NOMEASURE"],
Trajectory: tranjectory,
}
observations = append(observations, *observation)
}
case []models.RailwayContactRequest:
for _, contact := range contacts.([]models.RailwayContactRequest) {
tranjectory := fmt.Sprintf("在%s,与患者同期乘坐%s", contact.Launch.String(), contact.Train)
observation := &models.BackObservation{
Name: contact.Name,
Age: contact.Age,
Sex: contact.Sex,
Phone: contact.Phone,
Identification: contact.Identification,
Region: splitFakerAddress(contact.Address),
Address: "",
HealthSituation: global.HEALTH_SITUATION_ID_MAP["CONTACT"],
HealthChangeTime: time.Now(),
MeasureSituation: global.MEASURE_SITUATION_ID_MAP["NOMEASURE"],
Trajectory: tranjectory,
}
observations = append(observations, *observation)
}
}
return observations
}
+25
View File
@@ -0,0 +1,25 @@
package management
import (
"nCovTrack-Backend/models"
)
//InsertLocation Insert locations
func InsertLocation(claims models.TokenClaims, locationJMap map[string]interface{}) bool {
locationCMap := models.MapJ2c[models.BackLocation](locationJMap, true)
models.BeforeSave(locationCMap, claims.ID)
ok, rowsAffected := models.Upsert[models.BackLocation](locationCMap)
return (ok && rowsAffected != 0)
}
//ListLocation List locations
func ListLocation(queryJMap map[string]any) *[]map[string]any {
queryCMap := models.MapJ2c[models.BackLocation](queryJMap, true)
return models.ListField[models.BackLocation]([]map[string]any{queryCMap}, true, "is_delete")
}
//DeleteLocation Delete locations
func DeleteLocation(id int) bool {
ok, rowsAffected := models.DeleteById[models.BackLocation](id)
return (ok && rowsAffected != 0)
}
+389
View File
@@ -0,0 +1,389 @@
package management
import (
"fmt"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/service/notify"
"nCovTrack-Backend/utils"
"time"
)
//PullFromFaker Pull observations from faker system
func PullFromFaker(claims models.TokenClaims, patientId int) int64 {
// Get patient's identification by id
queryMap := []map[string]interface{}{{"id": patientId}}
patient := models.GetField[models.BackObservation](queryMap, false, "identification")
if patient == nil || patient["identification"] == nil {
return -1
}
// Pull contacts form faker
observations := queryContacts(patient["identification"].(string))
// Set ContactPerson
for i := range observations {
observations[i].ContactPerson = patientId
}
// Insert into db
var observationsJMap []map[string]interface{}
utils.Strcut2Map(observations, &observationsJMap)
observationsCMap := models.MapsJ2c[models.BackObservation](observationsJMap, false)
models.BeforeBatchSave(&observationsCMap, claims.ID)
_, rowsAffected := models.BatchInsert[models.BackObservation](observationsCMap)
// Generate situation record and send notify to the region
if rowsAffected != 0 {
pullSituationRecord(claims, observations)
pullNotify(observations)
}
return rowsAffected
}
//ListObservation list observations with query
func ListObservation(jsonMap map[string]interface{}) []models.ListObservation {
colMap := models.MapJ2c[models.BackObservation](jsonMap, true)
queryMap := []map[string]interface{}{colMap}
return listObservation(queryMap)
}
//GetObservation get observation by id
func GetObservation(id int) map[string]interface{} {
// Obseration
observationCMap := models.GetField[models.BackObservation]([]map[string]interface{}{{"id": id}}, true, "is_delete")
observationJMap := models.MapC2j[models.BackObservation](observationCMap, true)
// Pcr record
pcrRecordCMap := models.ListField[models.BackPcr]([]map[string]interface{}{{"observation": id}}, true, "is_delete")
pcrRecordJMap := models.MapsC2j[models.BackPcr](*pcrRecordCMap, true)
// Situation record
situationRecordCMap := models.ListField[models.BackSituationRecord]([]map[string]interface{}{{"observation": id}}, true, "is_delete")
situationRecordJMap := models.MapsC2j[models.BackSituationRecord](*situationRecordCMap, true)
// Assemble
observationJMap["pcrRecord"] = pcrRecordJMap
observationJMap["situationRecord"] = situationRecordJMap
return observationJMap
}
//InsertObservation add observation
func InsertObservation(claims models.TokenClaims, jsonMap map[string]interface{}) bool {
colMap := models.MapJ2c[models.BackObservation](jsonMap, true)
models.BeforeSave(colMap, claims.ID)
ok, rowsAffected := models.Upsert[models.BackObservation](colMap)
queryMap := []map[string]interface{}{{"identification": jsonMap["identification"]}}
// Get result after insert, assemble record will use it.
newObservation := models.GetField[models.BackObservation](queryMap, true)
recordOk := insertSituationRecord(claims, map[string]interface{}{}, newObservation)
return (ok && recordOk && rowsAffected != 0)
}
func UpdateObservation(claims models.TokenClaims, jsonMap map[string]any) bool {
// Pre actions
colMap := models.MapJ2c[models.BackObservation](jsonMap, true)
models.BeforeSave(colMap, claims.ID)
queryMap := []map[string]interface{}{{"id": jsonMap["id"]}}
// Get old record
oldMap := models.Get[models.BackObservation](queryMap)
if oldMap == nil || len(oldMap) == 0 {
return false
}
// Deal with region update
if colMap["region"] != nil && oldMap["region"].(string) != colMap["region"].(string) {
updateObservationRegion(colMap["region"].(string))
}
// Deal with situation update
if colMap["health_situation"] != nil && int(colMap["health_situation"].(float64)) != oldMap["health_situation"].(int) ||
colMap["measure_situation"] != nil && int(colMap["measure_situation"].(float64)) != oldMap["measure_situation"].(int) {
updateSituation(claims, oldMap, colMap)
}
ok, rowsAffected := models.Update[models.BackObservation](queryMap, colMap)
return ok && rowsAffected != 0
}
//TreeifyObservations get the transmission chains of observations
// Param direction: up, down, all
func TreeifyObservations(observationId int, direction string) *models.TreeObservation {
// Get arrays form back observation
var observations []models.BackObservation
if direction != "down" {
observations = append(observations, treeifyObservations(observationId, true)...)
}
if direction != "up" {
observations = append(observations, treeifyObservations(observationId, false)...)
}
// Treeify
observationMap := map[int]*models.TreeObservation{}
for _, observation := range observations {
observationMap[observation.ID] = &models.TreeObservation{
ID: observation.ID,
Name: observation.Name,
Age: observation.Age,
Sex: observation.Sex,
Phone: observation.Phone,
Identification: observation.Identification,
ContactPerson: observation.ContactPerson,
Region: observation.Region,
Address: observation.Address,
HealthSituation: observation.HealthSituation,
HealthChangeTime: observation.HealthChangeTime,
CreateUser: observation.CreateUser,
CreateTime: observation.CreateTime,
ModifyUser: observation.ModifyUser,
ModifyTime: observation.ModifyTime,
}
}
var rootKey int
for k := range observationMap {
parentId := observationMap[k].ContactPerson
parent := observationMap[parentId]
if parent == nil {
rootKey = k
continue
}
parent.Children = append(parent.Children, observationMap[k])
}
return observationMap[rootKey]
}
//treeifyObservations query tree data from db
// Param direction up: true, down: false
func treeifyObservations(observationId int, direction bool) []models.BackObservation {
var res []models.BackObservation
var directionCond string
if direction {
directionCond = "p.id = cte.contact_person"
} else {
directionCond = "cte.id = p.contact_person"
}
querySql := fmt.Sprintf(`
WITH RECURSIVE cte (id, name, age, sex, phone, identification, contact_person, region, address, health_situation, health_change_time, trajectory, create_user, modify_user, create_time, modify_time) AS (
SELECT id, name, age, sex, phone, identification, contact_person, region, address, health_situation, health_change_time, trajectory, create_user, modify_user, create_time, modify_time
FROM back_observation
WHERE contact_person = ?
UNION ALL
SELECT p.id, p.name, p.age, p.sex, p.phone, p.identification, p.contact_person, p.region, p.address, p.health_situation, p.health_change_time, p.trajectory, p.create_user, p.modify_user, p.create_time, p.modify_time
FROM back_observation p
INNER JOIN cte ON %s
)
SELECT * FROM cte
`, directionCond)
global.Db.Model(models.BackObservation{}).Raw(querySql, observationId).Scan(&res)
return res
}
//listObservation the queryMap need with table name
// Param queryMap the colMap of models.BackObservation
func listObservation(queryMap []map[string]interface{}) []models.ListObservation {
var observations []models.ListObservation
tx := global.Db.Model(new((models.BackObservation))).
Select("back_observation.*, back_pcr.detect_time AS pcr_time, detect_result AS pcr_result," +
"back_situation_record.create_time AS record_time, back_situation_record.record AS record").
Joins("LEFT JOIN back_pcr ON back_observation.id = back_pcr.observation").
Joins("LEFT JOIN back_situation_record ON back_observation.id = back_situation_record.observation")
for _, query := range queryMap {
query["is_delete"] = 0
// Add the table prefix
fullQuery := map[string]interface{}{}
for k := range query {
fullQuery["back_observation."+k] = query[k]
}
tx = tx.Or(fullQuery)
}
tx.Find(&observations)
return observations
}
//pullSituationRecord push situation record after pull observations
func pullSituationRecord(claims models.TokenClaims, observations []models.BackObservation) {
// Query observations' id
var identifications []string
for _, observation := range observations {
identifications = append(identifications, observation.Identification)
}
queryMap := []map[string]interface{}{{"identification": identifications}}
observationMaps := models.ListField[models.BackObservation](queryMap, false, "id", "identification")
// Assemble records
var records []models.BackSituationRecord
recordContent := record___20()
for _, observationMap := range *observationMaps {
record := models.BackSituationRecord{Observation: observationMap["id"].(int), Record: recordContent}
records = append(records, record)
}
// Insert into db
var recordsJMaps []map[string]interface{}
utils.Strcut2Map(records, &recordsJMaps)
recordsCMaps := models.MapsJ2c[models.BackSituationRecord](recordsJMaps, true)
models.BeforeBatchSave(&recordsCMaps, claims.ID)
models.BatchInsert[models.BackSituationRecord](recordsCMaps)
}
//pullNotify send notify after pull observations
func pullNotify(observations []models.BackObservation) {
var regions []string
for _, observation := range observations {
regions = append(regions, observation.Region)
}
regions = utils.Distinct(regions)
notification := models.BackNotification{Time: time.Now(), Kind: "疫情", Content: "出现了新的接触者,请及时登录系统进行处理"}
sendInfo := models.SendInfo{Region: regions, Channel: []int{0, 1}, Notification: notification}
notify.SendNotify(sendInfo)
}
//updateObservationRegion send notify while update observation's region
func updateObservationRegion(region string) {
notification := models.BackNotification{Time: time.Now(), Kind: "疫情", Content: "本地有新的接触者,请及时登录系统进行处理"}
sendInfo := models.SendInfo{Region: []string{region}, Channel: []int{0, 1}, Notification: notification}
notify.SendNotify(sendInfo)
}
//updateSituation make some special resolve while situation update
func updateSituation(claims models.TokenClaims, oldObservationMap, newObservationMap map[string]interface{}) {
if newObservationMap["health_situation"] != nil {
newHealthSituation := int(newObservationMap["health_situation"].(float64))
oldHealthSituation := oldObservationMap["health_situation"].(int)
// Update health situation change time and measure_situation
if newHealthSituation != oldHealthSituation {
newObservationMap["health_change_time"] = time.Now()
newObservationMap["measure_situation"] = 0
}
// Deal with contact to patient
if oldHealthSituation == 2 && newHealthSituation == 1 {
// Query sub contacts
queryMap := []map[string]interface{}{{"contact_person": oldObservationMap["id"].(int)}}
subContacts := models.ListField[models.BackObservation](queryMap, false, "id", "region")
// Get reginos and assemble situation record
var regions []string
var subContactRecords []models.BackSituationRecord
for _, subContact := range *subContacts {
regions = append(regions, subContact["region"].(string))
subContactRecord := models.BackSituationRecord{
Observation: int(newObservationMap["id"].(float64)),
Record: record___20(),
}
subContactRecords = append(subContactRecords, subContactRecord)
}
// Update sub contact
updateMap := map[string]interface{}{
"health_situation": 2,
"health_change_time": newObservationMap["health_change_time"],
"measure_situation": 0,
}
models.Update[models.BackObservation](queryMap, updateMap)
// Insert subContacts' record
var recordJMaps []map[string]interface{}
utils.Strcut2Map(subContactRecords, &recordJMaps)
recordCMap := models.MapsJ2c[models.BackSituationRecord](recordJMaps, true)
models.BeforeBatchSave(&recordCMap, claims.ID)
models.BatchInsert[models.BackSituationRecord](recordCMap)
// Send nofity
regions = utils.Distinct(regions)
notification := models.BackNotification{
Time: newObservationMap["health_change_time"].(time.Time),
Kind: "疫情",
Content: "由于密接者转为患者,次密接者状态需要修改",
}
sendInfo := models.SendInfo{
Region: regions,
Channel: []int{0, 1},
Notification: notification,
}
notify.SendNotify(sendInfo)
}
insertSituationRecord(claims, oldObservationMap, newObservationMap)
}
}
//insertSituationRecord insert situation record
func insertSituationRecord(claims models.TokenClaims, oldObservation, newObservation map[string]interface{}) bool {
var oldSituation, newSituation string
if oldObservation["id"] == nil {
// insert observation
oldSituation = "00"
} else {
// update observation
oldSituation = fmt.Sprintf("%d%d", oldObservation["health_situation"], oldObservation["measure_situation"])
}
if newObservation["health_situation"] == nil || newObservation["measure_situation"] == nil {
// Situation not update
return true
}
newSituation = fmt.Sprintf("%d%d", newObservation["health_situation"], newObservation["measure_situation"])
// Situation not update
if oldSituation == newSituation {
return true
}
// Set record content accrodding the situation convertions
var recordContent string
situationConvert := oldSituation + "_" + newSituation
switch situationConvert {
case "00_10":
recordContent = record00_10()
case "00_30":
recordContent = record00_30()
case "00_12":
recordContent = record00_12(newObservation["region"].(string), newObservation["address"].(string))
case "10_12":
recordContent = record10_12(newObservation["region"].(string), newObservation["address"].(string))
case "00_20", "33_20", "34_20":
recordContent = record___20()
case "12_01", "23_01", "24_01", "33_01", "34_01":
recordContent = record___01()
case "20_23", "30_33", "24_23", "34_33":
recordContent = record____3(newObservation["region"].(string), newObservation["address"].(string))
case "20_24", "30_34", "23_24", "33_34":
recordContent = record____4(newObservation["region"].(string), newObservation["address"].(string))
case "23_10", "24_10", "33_10", "34_10":
recordContent = record___10()
default:
if oldSituation != "00" {
models.Upsert[models.BackObservation](oldObservation)
} else {
models.DropById[models.BackObservation](newObservation["id"].(int))
}
return false
}
record := models.BackSituationRecord{Observation: newObservation["id"].(int), Record: recordContent}
var recordJMap map[string]interface{}
utils.Strcut2Map(record, &recordJMap)
recordCMap := models.MapJ2c[models.BackSituationRecord](recordJMap, true)
models.BeforeSave(recordCMap, claims.ID)
models.Upsert[models.BackSituationRecord](recordCMap)
return true
}
func record00_10() string {
date := utils.FormatDate(time.Now())
return date + " 确诊为患者"
}
func record00_12(region, address string) string {
date := utils.FormatDate(time.Now())
return fmt.Sprintf("%s 于 %s %s 确诊为患者", date, region, address)
}
func record___20() string {
date := utils.FormatDate(time.Now())
return date + " 转为密接"
}
func record00_30() string {
date := utils.FormatDate(time.Now())
return date + " 转为次密接"
}
func record___01() string {
date := utils.FormatDate(time.Now())
return fmt.Sprintf("%s 解除风险", date)
}
func record10_12(region, address string) string {
date := utils.FormatDate(time.Now())
return fmt.Sprintf("%s 转至医院:%s %s", date, region, address)
}
func record___10() string {
date := utils.FormatDate(time.Now())
return date + " 确诊为患者"
}
func record____3(region, address string) string {
date := utils.FormatDate(time.Now())
return fmt.Sprintf("%s 集中隔离至:%s %s", date, region, address)
}
func record____4(region, address string) string {
date := utils.FormatDate(time.Now())
return fmt.Sprintf("%s 进行居家隔离:%s %s", date, region, address)
}
+10
View File
@@ -0,0 +1,10 @@
package management
import "nCovTrack-Backend/models"
func InsertPcr(claims models.TokenClaims, jsonMap map[string]interface{}) bool {
colMap := models.MapJ2c[models.BackPcr](jsonMap, true)
models.BeforeSave(colMap, claims.ID)
ok, rowsAffected := models.Upsert[models.BackPcr](colMap)
return ok && rowsAffected != 0
}
+89
View File
@@ -0,0 +1,89 @@
package notify
import (
"encoding/json"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
)
type NotifyFunc func([]string, models.BackNotification)
const (
NOTIFY_KEY = "notification_"
NOTIFY_DEL_VALUE = "notify_del"
)
var (
NOTIFY_CHANNEL_FUNC_ARR = []NotifyFunc{notifyNotification, emailNotification}
)
//emailNotification send email
func emailNotification(regions []string, notification models.BackNotification) {
queryMap := []map[string]interface{}{{"region": regions, "role": global.ROLE_ID_MAP["ADMIN"]}}
observations := models.ListField[models.BackUser](queryMap, false, "email")
var emails []string
for _, observation := range *observations {
emails = append(emails, observation["email"].(string))
}
utils.SendEmail(notification.Kind+"通知邮件", notification.Content, emails...)
}
//notifyNotification add to notify system
func notifyNotification(regions []string, notification models.BackNotification) {
valueByte, _ := json.Marshal(notification)
pipe := global.Redis.TxPipeline()
for _, region := range regions {
pipe.RPush(NOTIFY_KEY+region, valueByte)
}
pipe.Exec()
}
//DeleteNotification delete notify by id
func DeleteNotification(region string, index int) {
key := NOTIFY_KEY + region
pipe := global.Redis.TxPipeline()
pipe.LSet(key, int64(index), NOTIFY_DEL_VALUE)
pipe.LRem(key, 2, NOTIFY_DEL_VALUE)
pipe.Exec()
}
//CleanNotification delete all notify
func CleanNotification(region string) {
key := NOTIFY_KEY + region
global.Redis.Del(key)
}
//QueryNotificationLen query the count notification
func QueryNotificationLen(region string) int {
key := NOTIFY_KEY + region
return int(global.Redis.LLen(key).Val())
}
//ListNotifycation list the notifications ny range
func ListNotifycation(region string, start, end int) []models.BackNotification {
key := NOTIFY_KEY + region
var notifications []models.BackNotification
notificationStrArr := global.Redis.LRange(key, int64(start), int64(end)).Val()
for _, notificationStr := range notificationStrArr {
var notification models.BackNotification
json.Unmarshal([]byte(notificationStr), &notification)
notifications = append(notifications, notification)
}
return notifications
}
//SendNotify send notify to kafka
func SendNotify(send models.SendInfo) {
sendInfo, _ := json.Marshal(send)
global.KafkaProducerChan <- sendInfo
}
//HandleKafkaNotify handle kafka info
func HandleKafkaNotify(sendInfoByte []byte) {
var sendInfo models.SendInfo
json.Unmarshal(sendInfoByte, &sendInfo)
for _, channel := range sendInfo.Channel {
NOTIFY_CHANNEL_FUNC_ARR[channel](sendInfo.Region, sendInfo.Notification)
}
}
+42 -24
View File
@@ -17,21 +17,23 @@ const (
rds_CHINA_ADD_KEY = "chinaAdd"
rds_COUNTRY_LEVEL_KEY = "countryLevel"
rds_COUNTRY_LEVEL_CHILD_KEY = "countryLevelChild"
rds_PROVIENCE_LEVEL_CHILD_KEY = "provienceLevelChild"
rds_PROVIENCE_LEVEL_NOW_CONFIRM_KEY = "provienceLevelNowConfirm"
rds_PROVIENCE_LEVEL_TODAY_CONFIRM_KEY = "provienceLevelTodayConfirm"
rds_PROVIENCE_LEVEL_TOTAL_CONFIRM_KEY = "provienceLevelTotalConfirm"
rds_PROVINCE_LEVEL_CHILD_KEY = "provinceLevelChild"
rds_PROVINCE_LEVEL_NOW_CONFIRM_KEY = "provinceLevelNowConfirm"
rds_PROVINCE_LEVEL_TODAY_CONFIRM_KEY = "provinceLevelTodayConfirm"
rds_PROVINCE_LEVEL_TOTAL_CONFIRM_KEY = "provinceLevelTotalConfirm"
rds_CITY_LEVEL_CHILD_KEY = "cityLevelChild"
rds_CITY_LEVEL_NOW_CONFIRM_KEY = "cityLevelNowConfirm"
rds_CITY_LEVEL_TODAY_CONFIRM_KEY = "cityLevelTodayConfirm"
rds_CITY_LEVEL_TOTAL_CONFIRM_KEY = "cityLevelTotalConfirm"
rds_LAST_UPDATE_TIME = "statisticsLastUpdateTime"
rds_LAST_CACHE_TIME = "statisticsLastCacheTime"
rds_CHINA_DAY_ADD_LIST_KEY = "chinaDayAdd"
rds_CHINA_DAY_LIST_KEY = "chinaDay"
SORT_TODAY_CONFIRM = "today"
SORT_TOTAL_CONFIRM = "total"
SORT_NOW_CONFIRM = "now"
D
json_FOREIGN_COUNTRY = "境外"
json_FOREIGN_CITY = "外地"
json_TO_BE_CONFIRM = "待确认"
@@ -45,16 +47,31 @@ type AreaSlice []models.AreaInfo
func cacheNCovStatistics() {
resp := utils.GetWhioutHeader(global.CHINA_NCOV_STATISTIC_URL)
var nCovRes map[string]string
var nCovRes map[string]interface{}
json.Unmarshal([]byte(resp), &nCovRes)
var nCovResData map[string]interface{}
json.Unmarshal([]byte(nCovRes["data"]), &nCovResData)
nCovResData := (nCovRes["data"].(map[string]interface{}))["diseaseh5Shelf"].(map[string]interface{})
if !needToRecache(nCovResData) {
return
}
cacheChinaInfo(nCovResData)
cacheLevelInfo(nCovResData)
cacheLastUpdateTime(nCovResData)
resp = utils.GetWhioutHeader(global.CHINA_NCOV_STATISTIC_TREND_URL)
}
func CacheNCovTrend() {
resp := utils.GetWhioutHeader(global.CHINA_NCOV_STATISTIC_TREND_URL)
var nCovRes map[string]interface{}
json.Unmarshal([]byte(resp), &nCovRes)
nCovResData := nCovRes["data"].(map[string]interface{})
cacheChinaTrend(nCovResData)
}
func cacheChinaTrend(data map[string]interface{}) {
chinaDayAdd, _ := json.Marshal(data["chinaDayAddList"])
chinaDay, _ := json.Marshal(data["chinaDayList"])
global.Redis.Set(rds_CHINA_DAY_ADD_LIST_KEY, chinaDayAdd, 0)
global.Redis.Set(rds_CHINA_DAY_LIST_KEY, chinaDay, 0)
}
func cacheChinaInfo(data map[string]interface{}) {
@@ -81,8 +98,8 @@ func cacheLevelInfo(data map[string]interface{}) {
// Get Every Level's Info
var countryLevels []models.AreaInfo
json.Unmarshal(areaTree, &countryLevels)
provienceLevels := children(countryLevels)
cityLevels := children(provienceLevels)
provinceLevels := children(countryLevels)
cityLevels := children(provinceLevels)
// Country Level Area Info With Child
cacheList(rds_COUNTRY_LEVEL_CHILD_KEY, areaInfoToJson(countryLevels)...)
@@ -90,34 +107,34 @@ func cacheLevelInfo(data map[string]interface{}) {
areaInfoChildNil(&countryLevels)
cacheList(rds_COUNTRY_LEVEL_KEY, areaInfoToJson(countryLevels)...)
// Provience Level Area Info With Child
cacheList(rds_PROVIENCE_LEVEL_CHILD_KEY, areaInfoToJson(provienceLevels)...)
areaInfoChildNil(&provienceLevels)
// Province Level Area Info With Child
cacheList(rds_PROVINCE_LEVEL_CHILD_KEY, areaInfoToJson(provinceLevels)...)
areaInfoChildNil(&provinceLevels)
// City Level Area Info With Child
cacheList(rds_CITY_LEVEL_CHILD_KEY, areaInfoToJson(cityLevels)...)
areaInfoChildNil(&provienceLevels)
areaInfoChildNil(&provinceLevels)
// Provience Level Area Info Sorted by Now Confirm
provienceLevelsSlice := AreaSlice(provienceLevels)
sort.Sort(provienceLevelsSlice)
cacheList(rds_PROVIENCE_LEVEL_NOW_CONFIRM_KEY, areaInfoToJson(provienceLevelsSlice)...)
// Province Level Area Info Sorted by Now Confirm
provinceLevelsSlice := AreaSlice(provinceLevels)
sort.Sort(provinceLevelsSlice)
cacheList(rds_PROVINCE_LEVEL_NOW_CONFIRM_KEY, areaInfoToJson(provinceLevelsSlice)...)
// City Level Area Info Sorted By Now Confirm
cityLevelsSlice := AreaSlice(cityLevels)
sort.Sort(cityLevelsSlice)
cacheList(rds_CITY_LEVEL_NOW_CONFIRM_KEY, areaInfoToJson(cityLevelsSlice)...)
sortBy = SORT_TODAY_CONFIRM
// Provience Level Area Info Sorted by Today Confirm
sort.Sort(provienceLevelsSlice)
cacheList(rds_PROVIENCE_LEVEL_TODAY_CONFIRM_KEY, areaInfoToJson(provienceLevelsSlice)...)
// Province Level Area Info Sorted by Today Confirm
sort.Sort(provinceLevelsSlice)
cacheList(rds_PROVINCE_LEVEL_TODAY_CONFIRM_KEY, areaInfoToJson(provinceLevelsSlice)...)
// City Level Area Info Sorted by Today Confirm
sort.Sort(cityLevelsSlice)
cacheList(rds_CITY_LEVEL_TODAY_CONFIRM_KEY, areaInfoToJson(cityLevelsSlice)...)
sortBy = SORT_TOTAL_CONFIRM
// Provience Level Area Info Sorted by Total Confirm
sort.Sort(provienceLevelsSlice)
cacheList(rds_PROVIENCE_LEVEL_TOTAL_CONFIRM_KEY, areaInfoToJson(provienceLevelsSlice)...)
// Province Level Area Info Sorted by Total Confirm
sort.Sort(provinceLevelsSlice)
cacheList(rds_PROVINCE_LEVEL_TOTAL_CONFIRM_KEY, areaInfoToJson(provinceLevelsSlice)...)
// City Level Area Info Sorted by Total Confirm
sort.Sort(cityLevelsSlice)
cacheList(rds_CITY_LEVEL_TOTAL_CONFIRM_KEY, areaInfoToJson(cityLevelsSlice)...)
@@ -137,6 +154,7 @@ func children(parents []models.AreaInfo) []models.AreaInfo {
}
for _, item := range parent.Children {
name := item.Name
item.Parent = parent.Name
if !strings.Contains(name, json_FOREIGN_CITY) && !strings.Contains(name, json_FOREIGN_COUNTRY) && !strings.Contains(name, json_TO_BE_CONFIRM) {
areaInfos = append(areaInfos, item)
}
+33 -5
View File
@@ -7,18 +7,18 @@ import (
"strings"
)
func GetAllProvienceData(sort string) []interface{} {
func GetAllProvinceData(sort string) []interface{} {
checkCache()
if sort == SORT_TODAY_CONFIRM {
return getEntireRedisList(rds_PROVIENCE_LEVEL_TODAY_CONFIRM_KEY)
return getEntireRedisList(rds_PROVINCE_LEVEL_TODAY_CONFIRM_KEY)
}
if sort == SORT_TOTAL_CONFIRM {
return getEntireRedisList(rds_PROVIENCE_LEVEL_TOTAL_CONFIRM_KEY)
return getEntireRedisList(rds_PROVINCE_LEVEL_TOTAL_CONFIRM_KEY)
}
if sort == SORT_NOW_CONFIRM {
return getEntireRedisList(rds_PROVIENCE_LEVEL_NOW_CONFIRM_KEY)
return getEntireRedisList(rds_PROVINCE_LEVEL_NOW_CONFIRM_KEY)
}
return getEntireRedisList(rds_PROVIENCE_LEVEL_CHILD_KEY)
return getEntireRedisList(rds_PROVINCE_LEVEL_CHILD_KEY)
}
func GetAllCityData(sort string) []interface{} {
@@ -36,6 +36,7 @@ func GetAllCityData(sort string) []interface{} {
}
func GetCountryData(child bool) []interface{} {
checkCache()
if child {
return getEntireRedisList(rds_COUNTRY_LEVEL_CHILD_KEY)
}
@@ -43,12 +44,39 @@ func GetCountryData(child bool) []interface{} {
}
func GetChinaNCovStatistic() models.ChinaData {
checkCache()
data := models.ChinaData{}
json.Unmarshal([]byte(global.Redis.Get(rds_CHINA_ADD_KEY).Val()), &data.ChinaAdd)
json.Unmarshal([]byte(global.Redis.Get(rds_CHINA_TOTAL_KEY).Val()), &data.ChinaTotal)
return data
}
func GetChinaDayAdd() []models.ChinaDayAdd {
chinaDayAddStr := global.Redis.Get(rds_CHINA_DAY_ADD_LIST_KEY).Val()
if chinaDayAddStr == "" {
CacheNCovTrend()
chinaDayAddStr = global.Redis.Get(rds_CHINA_DAY_ADD_LIST_KEY).Val()
}
var chinaDayAddList []models.ChinaDayAdd
json.Unmarshal([]byte(chinaDayAddStr), &chinaDayAddList)
return chinaDayAddList
}
func GetChinaDay() []models.ChinaDay {
chinaDayStr := global.Redis.Get(rds_CHINA_DAY_LIST_KEY).Val()
if chinaDayStr == "" {
CacheNCovTrend()
chinaDayStr = global.Redis.Get(rds_CHINA_DAY_LIST_KEY).Val()
}
var chinaDayList []models.ChinaDay
json.Unmarshal([]byte(chinaDayStr), &chinaDayList)
return chinaDayList
}
func GetChinaTrend() models.ChinaTrend {
return models.ChinaTrend{ChinaDayAddList: GetChinaDayAdd(), ChinaDayList: GetChinaDay()}
}
func getEntireRedisList(key string) []interface{} {
var data []interface{}
dataStrArr := global.Redis.LRange(key, 0, -1).Val()
+63 -37
View File
@@ -1,15 +1,16 @@
package user
import (
"encoding/json"
"fmt"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/service/notify"
"nCovTrack-Backend/utils"
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)
const (
@@ -29,64 +30,88 @@ func Login(user map[string]interface{}) (token string) {
if userInfo == nil {
return ""
}
if userInfo["approver"].(int) <= 0 {
return ""
}
if !utils.PasswordCompare(user["password"].(string), userInfo["password"].(string)) {
return ""
}
claims := jwt.MapClaims{
"id": userInfo["id"],
"username": userInfo["username"],
"role": userInfo["role"],
"email": userInfo["email"],
"region": userInfo["region"],
"role": userInfo["role"],
}
return utils.GenerateToken(claims)
}
// Register user register, user can use account after approved
func Register(user map[string]interface{}) {
func Register(user map[string]interface{}) bool {
user["password"] = utils.PasswordEncrypt(user["password"].(string))
userStr, _ := json.Marshal(user)
// insert into redis, wait for approve
cmd := global.Redis.HMSet(global.REGISTER_REDIS_KEY, map[string]interface{}{user["email"].(string): userStr})
if cmd.Err() != nil {
panic(cmd.Err())
user["approver"] = 0
colMap := models.MapJ2c[models.BackUser](user, false)
ok, rowsAffected := models.Upsert[models.BackUser](colMap)
var sendRegion string
if int(user["role"].(float64)) == global.ROLE_ID_MAP["ADMIN"] {
sendRegion = strings.Split(user["region"].(string), " ")[0]
} else {
sendRegion = user["region"].(string)
}
notification := models.BackNotification{Time: time.Now(), Kind: "审批", Content: "有新的注册待审批"}
sendInfo := models.SendInfo{Region: []string{sendRegion}, Channel: []int{0}, Notification: notification}
notify.SendNotify(sendInfo)
if !ok || rowsAffected == 0 {
return false
}
return true
}
// ListRegister list the registers in the redis to be approved
func ListRegister() *[]map[string]interface{} {
applyStrMap := global.Redis.HGetAll(global.REGISTER_REDIS_KEY).Val()
var applies []map[string]interface{}
for _, v := range applyStrMap {
var apply map[string]interface{}
_ = json.Unmarshal([]byte(v), &apply)
applies = append(applies, apply)
func ListRegister(claims models.TokenClaims) *[]map[string]interface{} {
registers := []map[string]interface{}{}
tx := global.Db.Model(new(models.BackUser)).Omit("password")
if claims.Region == "" {
// do nothing
} else if !strings.Contains(claims.Region, " ") {
// Province Admin approve city admin
tx.Where("approver = 0 AND is_delete = 0 AND region LIKE ? AND role = ?", claims.Region+" %", global.ROLE_ID_MAP["ADMIN"])
registers = *models.ListByOrm(tx)
} else {
// City Admin approve workers and volunteers
tx.Where("approver = 0 AND is_delete = 0 AND region = ? AND role in ?", claims.Region, []int{global.ROLE_ID_MAP["WORKER"], global.ROLE_ID_MAP["VOLUNTEER"]})
registers = *models.ListByOrm(tx)
}
if applies == nil {
applies = []map[string]interface{}{}
return &registers
}
return &applies
// ListApprovedRegister list registers approved by the admin
func ListApprovedRegister(claims models.TokenClaims) *[]map[string]interface{} {
approvedRegisters := []map[string]interface{}{}
tx := global.Db.Model(new(models.BackUser)).Omit("password").Where("approver in ? and is_delete = 0", []int{claims.ID, -claims.ID})
approvedRegisters = *models.ListByOrm(tx)
return &approvedRegisters
}
// ApproveRegister approve a register
func ApproveRegister(email string, pass bool) bool {
if !pass {
rowsAffected := global.Redis.HDel(global.REGISTER_REDIS_KEY, email).Val()
return rowsAffected != 0
func ApproveRegister(claims models.TokenClaims, email string, pass bool) bool {
queryMap := []map[string]interface{}{{"email": email}}
var approver int
// Approver field status:
// = 0: not approved
// > 0: approver id, and registe successful
// < 0: approver id, and registe failed
if pass {
approver = claims.ID
} else {
approver = -claims.ID
}
// if pass, will get the register info from redis, and the insert into mysql, this mean user is register success
applyStr := global.Redis.HGet(global.REGISTER_REDIS_KEY, email).Val()
rowsAffected := global.Redis.HDel(global.REGISTER_REDIS_KEY, email).Val()
if rowsAffected == 0 {
updateMap := map[string]interface{}{"approver": approver, "modify_time": time.Now()}
ok, rowsAffected := models.Update[models.BackUser](queryMap, updateMap)
if !ok || rowsAffected == 0 {
return false
}
var apply map[string]interface{}
_ = json.Unmarshal([]byte(applyStr), &apply)
if !NoDuplicatePhoneOrEmail(apply["phone"].(string), apply["email"].(string)) {
return false
}
colMap := models.MapJ2c[models.BackUser](apply, true)
ok, rowsAffected := models.Upsert[models.BackUser](colMap)
return ok && rowsAffected != 0
return true
}
// ChangePassword user change password, or user forgot password
@@ -97,11 +122,12 @@ func ChangePassword(changePwd map[string]interface{}) bool {
}
newPassword := utils.PasswordEncrypt(changePwd["newPassword"].(string))
colMap := map[string]interface{}{
"id": 1,
"id": 1.0,
"password": newPassword,
}
models.BeforeSave(colMap, -1)
delete(colMap, "id")
delete(colMap, "modify_user")
rowAffected := global.Db.Model(models.BackUser{}).Where("email = ?", changePwd["email"]).Updates(colMap).RowsAffected
if rowAffected == 0 {
return false
+11
View File
@@ -27,3 +27,14 @@ email:
port: 587
account: fallen-angle@foxmail.com
password: hxrisxltxsjvieec
bos:
accessKey: 90dbff87c5aa4bdbb0d7a29e130b2808
secretKey: e53a672a10294abc8ecabe1ef92625b1
domain: bj.bcebos.com
kafka:
servers:
- myhost.fallen-angle.com:9092
topic: ncov_track
partition: 0
+15
View File
@@ -0,0 +1,15 @@
package utils
import "nCovTrack-Backend/global"
func UploadFile() string {
etag, err := global.BosClient.PutObjectFromFile("ncovtrack", "test.jpg", "/home/fallen-angle/Pictures/Anime/pic-w-000003.jpg", nil)
if err != nil {
return ""
}
return etag
}
func DownloadLink() string {
return global.BosClient.BasicGeneratePresignedUrl("ncovtrack", "test.jpg", 1800)
}
+1 -1
View File
@@ -8,7 +8,6 @@ import (
)
func SendEmail(subject string, text string, to ...string) bool {
//TODO: add logs
e := email.Email{
From: "nCovTrack Server<1853633282@qq.com>",
To: to,
@@ -20,6 +19,7 @@ func SendEmail(subject string, text string, to ...string) bool {
smtp.PlainAuth("", global.ServerSettings.Email.Account, global.ServerSettings.Email.Password, global.ServerSettings.Email.Host),
)
if err != nil {
fmt.Println("Send failed")
return false
}
return true
+5 -1
View File
@@ -31,7 +31,11 @@ func GetWithHeader(url string, header map[string]string) string {
cost := time.Since(startTime)
var logParams []interface{}
logParams = append(logParams,
"reqest", req,
"reqest", map[string]interface{}{
"url": req.URL,
"method": req.Method,
"header": req.Header,
},
"cost", cost.String(),
)
if err != nil {
+10
View File
@@ -0,0 +1,10 @@
package utils
import (
"encoding/json"
)
func Strcut2Map[S any, T *map[string]interface{} | *[]map[string]interface{}](source S, target T) {
jsonByte, _ := json.Marshal(source)
json.Unmarshal(jsonByte, target)
}
+10 -2
View File
@@ -1,10 +1,12 @@
package utils
import (
"fmt"
"encoding/json"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
@@ -56,7 +58,7 @@ func RenewToken(tokenStr string) string {
if expireDuration.Hours() > float64(global.ServerSettings.Jwt.RenewAheadDays*24) {
return tokenStr
}
fmt.Println(expireDuration)
//fmt.Println(expireDuration)
claims["exp"] = time.Now().Add(global.TOKEN_EXPIRE_DAYS * 24 * time.Hour).Unix()
token = jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
@@ -81,3 +83,9 @@ func ParseClaims(tokenStr string) jwt.MapClaims {
}
return token.Claims.(jwt.MapClaims)
}
func ClaimsFromHeader(c *gin.Context) models.TokenClaims {
var claims models.TokenClaims
json.Unmarshal([]byte(c.Request.Header.Get("claims")), &claims)
return claims
}
+21
View File
@@ -15,3 +15,24 @@ func Map[T any, V any](arr []T, fun func(item T) V) []V {
}
return res
}
func Distinct[T comparable](arr []T) []T {
set := map[T]interface{}{}
for _, item := range arr {
set[item] = nil
}
var res []T
for k := range set {
res = append(res, k)
}
return res
}
func Contains[T comparable](arr []T, item T) bool {
for _, a := range arr {
if a == item {
return true
}
}
return false
}
-40
View File
@@ -1,40 +0,0 @@
package utils
type void struct{}
// This is set with generic
type Set[T comparable] struct {
setMap map[T]void
}
func NewSet[T comparable](eles ...T) *Set[T] {
set := &Set[T]{setMap: make(map[T]void)}
set.AddAll(eles...)
return set
}
func (set *Set[T]) Add(ele T) *Set[T] {
set.setMap[ele] = void{}
return set
}
func (set *Set[T]) AddAll(eles ...T) *Set[T] {
for _, ele := range eles {
set.Add(ele)
}
return set
}
func (set *Set[T]) Delete(ele T) *Set[T] {
delete(set.setMap, ele)
return set
}
func (set *Set[T]) ToSlice() []T {
var s []T
for k, _ := range set.setMap {
s = append(s, k)
}
return s
}