feat: management && notify

This commit is contained in:
fallen-angle
2022-05-04 20:06:21 +08:00
parent e58bafd0d3
commit 7598280fc1
28 changed files with 1226 additions and 143 deletions

View File

@@ -310,6 +310,37 @@ const docTemplate = `{
}
}
},
"/statistics/china/trend": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Statistics"
],
"summary": "china trend",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.GinResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.ChinaTrend"
}
}
}
]
}
}
}
}
},
"/statistics/city/{sort}": {
"get": {
"produces": [
@@ -741,6 +772,9 @@ const docTemplate = `{
"name": {
"type": "string"
},
"parent": {
"type": "string"
},
"today": {
"$ref": "#/definitions/models.AreaToday"
},
@@ -939,6 +973,100 @@ const docTemplate = `{
}
}
},
"models.ChinaDay": {
"type": "object",
"properties": {
"confirm": {
"type": "integer"
},
"date": {
"type": "string"
},
"dead": {
"type": "integer"
},
"deadRate": {
"type": "string"
},
"heal": {
"type": "integer"
},
"healRate": {
"type": "string"
},
"importedCase": {
"type": "integer"
},
"localConfirm": {
"type": "integer"
},
"localConfirmH5": {
"type": "integer"
},
"local_acc_confirm": {
"type": "integer"
},
"noInfect": {
"type": "integer"
},
"noInfectH5": {
"type": "integer"
},
"nowConfirm": {
"type": "integer"
},
"nowSevere": {
"type": "integer"
},
"suspect": {
"type": "integer"
},
"y": {
"type": "string"
}
}
},
"models.ChinaDayAdd": {
"type": "object",
"properties": {
"confirm": {
"type": "integer"
},
"date": {
"type": "string"
},
"dead": {
"type": "integer"
},
"deadRate": {
"type": "string"
},
"heal": {
"type": "integer"
},
"healRate": {
"type": "string"
},
"importedCase": {
"type": "integer"
},
"infect": {
"type": "integer"
},
"localConfirmadd": {
"type": "integer"
},
"localinfectionadd": {
"type": "integer"
},
"suspect": {
"type": "integer"
},
"y": {
"type": "string"
}
}
},
"models.ChinaTotal": {
"type": "object",
"properties": {
@@ -986,6 +1114,23 @@ const docTemplate = `{
}
}
},
"models.ChinaTrend": {
"type": "object",
"properties": {
"ChinaDayList": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ChinaDay"
}
},
"chinaDayAddList": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ChinaDayAdd"
}
}
}
},
"models.ListArticle": {
"type": "object",
"properties": {

View File

@@ -300,6 +300,37 @@
}
}
},
"/statistics/china/trend": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Statistics"
],
"summary": "china trend",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/utils.GinResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/models.ChinaTrend"
}
}
}
]
}
}
}
}
},
"/statistics/city/{sort}": {
"get": {
"produces": [
@@ -731,6 +762,9 @@
"name": {
"type": "string"
},
"parent": {
"type": "string"
},
"today": {
"$ref": "#/definitions/models.AreaToday"
},
@@ -929,6 +963,100 @@
}
}
},
"models.ChinaDay": {
"type": "object",
"properties": {
"confirm": {
"type": "integer"
},
"date": {
"type": "string"
},
"dead": {
"type": "integer"
},
"deadRate": {
"type": "string"
},
"heal": {
"type": "integer"
},
"healRate": {
"type": "string"
},
"importedCase": {
"type": "integer"
},
"localConfirm": {
"type": "integer"
},
"localConfirmH5": {
"type": "integer"
},
"local_acc_confirm": {
"type": "integer"
},
"noInfect": {
"type": "integer"
},
"noInfectH5": {
"type": "integer"
},
"nowConfirm": {
"type": "integer"
},
"nowSevere": {
"type": "integer"
},
"suspect": {
"type": "integer"
},
"y": {
"type": "string"
}
}
},
"models.ChinaDayAdd": {
"type": "object",
"properties": {
"confirm": {
"type": "integer"
},
"date": {
"type": "string"
},
"dead": {
"type": "integer"
},
"deadRate": {
"type": "string"
},
"heal": {
"type": "integer"
},
"healRate": {
"type": "string"
},
"importedCase": {
"type": "integer"
},
"infect": {
"type": "integer"
},
"localConfirmadd": {
"type": "integer"
},
"localinfectionadd": {
"type": "integer"
},
"suspect": {
"type": "integer"
},
"y": {
"type": "string"
}
}
},
"models.ChinaTotal": {
"type": "object",
"properties": {
@@ -976,6 +1104,23 @@
}
}
},
"models.ChinaTrend": {
"type": "object",
"properties": {
"ChinaDayList": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ChinaDay"
}
},
"chinaDayAddList": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ChinaDayAdd"
}
}
}
},
"models.ListArticle": {
"type": "object",
"properties": {

View File

@@ -7,6 +7,8 @@ definitions:
type: array
name:
type: string
parent:
type: string
today:
$ref: '#/definitions/models.AreaToday'
total:
@@ -144,6 +146,68 @@ definitions:
chinaTotal:
$ref: '#/definitions/models.ChinaTotal'
type: object
models.ChinaDay:
properties:
confirm:
type: integer
date:
type: string
dead:
type: integer
deadRate:
type: string
heal:
type: integer
healRate:
type: string
importedCase:
type: integer
local_acc_confirm:
type: integer
localConfirm:
type: integer
localConfirmH5:
type: integer
noInfect:
type: integer
noInfectH5:
type: integer
nowConfirm:
type: integer
nowSevere:
type: integer
suspect:
type: integer
"y":
type: string
type: object
models.ChinaDayAdd:
properties:
confirm:
type: integer
date:
type: string
dead:
type: integer
deadRate:
type: string
heal:
type: integer
healRate:
type: string
importedCase:
type: integer
infect:
type: integer
localConfirmadd:
type: integer
localinfectionadd:
type: integer
suspect:
type: integer
"y":
type: string
type: object
models.ChinaTotal:
properties:
confirm:
@@ -175,6 +239,17 @@ definitions:
suspect:
type: integer
type: object
models.ChinaTrend:
properties:
ChinaDayList:
items:
$ref: '#/definitions/models.ChinaDay'
type: array
chinaDayAddList:
items:
$ref: '#/definitions/models.ChinaDayAdd'
type: array
type: object
models.ListArticle:
properties:
cover:
@@ -422,6 +497,23 @@ paths:
summary: china data
tags:
- Statistics
/statistics/china/trend:
get:
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/utils.GinResponse'
- properties:
data:
$ref: '#/definitions/models.ChinaTrend'
type: object
summary: china trend
tags:
- Statistics
/statistics/city/{sort}:
get:
parameters:

View File

@@ -35,10 +35,11 @@ func GetHttpClient(key string) (*http.Client, error) {
}
const (
CHINA_NCOV_STATISTIC_URL = "https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=statisGradeCityDetail,diseaseh5Shelf"
ENV_NOLOG = "nolog"
TOKEN_EXPIRE_DAYS = 15
FACKER_HOST = "http://myhost.fallen-angle.com:5000/"
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"
@@ -57,4 +58,22 @@ var (
"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,
}
)

View File

@@ -9,11 +9,13 @@ import (
// This file is define some business error
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"
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{}) {
@@ -40,3 +42,7 @@ func Forbidden(c *gin.Context) {
func UrlNotFound(c *gin.Context) {
c.String(http.StatusNotFound, PAGE_NOT_FOUND)
}
func OperationFailed(c *gin.Context) {
c.String(STATUS_OPERATION_FAILED, OPERATION_FAILED)
}

68
handler/management.go Normal file
View File

@@ -0,0 +1,68 @@
package handler
import (
"nCovTrack-Backend/service/management"
"nCovTrack-Backend/utils"
"strconv"
"github.com/gin-gonic/gin"
)
func PullContactHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
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})
}
func ListObservationsHandler(c *gin.Context) {
jsonMap := bindQuery(c)
utils.Succ(c, management.ListObservation(jsonMap))
}
func InsertObservationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
observaion := bindJson(c)
delete(observaion, "id")
ok := management.InsertObservation(claims, observaion)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
func GetObservationHandler(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
utils.Succ(c, management.GetObservation(id))
}
func ListLocationHandler(c *gin.Context) {
queryMap := bindQuery(c)
utils.Succ(c, management.ListLocation(queryMap))
}
func InsertLocationHandler(c *gin.Context) {
claims := utils.ClaimsFromHeader(c)
locationMap := bindJson(c)
ok := management.InsertLocation(claims, locationMap)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}
func DeleteLocationHandler(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
ok := management.DeleteLocation(id)
if ok {
utils.Succ(c, nil)
} else {
OperationFailed(c)
}
}

View File

@@ -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)
}

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
}

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()
}

View File

@@ -1,63 +0,0 @@
package models
import (
"fmt"
"time"
)
type FakerDate time.Time
const (
timeFormat = "2006-01-02"
)
func (t *FakerDate) UnmarshalJSON(data []byte) (err error) {
fmt.Println(string(data))
newTime, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
*t = FakerDate(newTime)
return
}
func (t FakerDate) MarshalJSON() ([]byte, error) {
fmt.Println(time.Time(t).Format(timeFormat))
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 int `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"`
}

156
models/management.go Normal file
View File

@@ -0,0 +1,156 @@
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 ListObeservation 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"` //核酸结果
RecordTime time.Time `json:"recordTime"` //状态转换时间
Record string `json:"record"` //状态转换内容
}
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"`
}

9
models/notify.go Normal file
View File

@@ -0,0 +1,9 @@
package models
import "time"
type BackNotification struct {
Time time.Time `json:"time"`
Kind int `json:"kind"`
Content string `json:"content"`
}

View File

@@ -59,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"`
}

View File

@@ -60,17 +60,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 +124,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 || int(colMap["id"].(float64)) == 0 {
tx = global.Db.Model(new(T)).Create(colMap)
} else {
tx = global.Db.Model(new(T)).Where("id = ?", colMap["id"]).Updates(colMap)
@@ -93,6 +136,15 @@ 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 {
@@ -120,6 +172,10 @@ func DeleteById[T any](id int) (ok bool, rowsAffected int64) {
return true, 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{} {
tx := global.Db.Model(new(T))
for _, e := range queryMap {

View File

@@ -1,17 +0,0 @@
package router
import (
"github.com/gin-gonic/gin"
"nCovTrack-Backend/service/investigate"
"net/http"
)
func investigatePublicRouter(router *gin.RouterGroup) {
investigateRouter := router.Group("investigate")
{
investigateRouter.GET("/test", func(c *gin.Context) {
investigate.QueryHotelContacts()
c.JSON(http.StatusOK, nil)
})
}
}

17
router/management.go Normal file
View File

@@ -0,0 +1,17 @@
package router
import (
"nCovTrack-Backend/handler"
"github.com/gin-gonic/gin"
)
func managementPublicRouter(router *gin.RouterGroup) {
managementRouter := router.Group("/management")
{
managementRouter.GET("/observation/contact/pull/:patientId", handler.PullContactHandler)
managementRouter.GET("/observation", handler.ListObservationsHandler)
managementRouter.POST("/observation", handler.InsertObservationHandler)
managementRouter.GET("/observation/:id", handler.GetObservationHandler)
}
}

View File

@@ -15,7 +15,7 @@ func BusiRouter() {
statisticRouter(publicRouter)
articlePublicRouter(publicRouter)
userPublicRouter(publicRouter)
investigatePublicRouter(publicRouter)
managementPublicRouter(publicRouter)
}
// Private

View File

@@ -13,5 +13,6 @@ func statisticRouter(router *gin.RouterGroup) {
statisticsRouter.GET("/country/child", handler.CountryDataHandler)
statisticsRouter.GET("/country", handler.CountryDataHandler)
statisticsRouter.GET("/china", handler.ChinaDataHandler)
statisticsRouter.GET("/china/trend", handler.ChinaTrendHandler)
}
}

View File

@@ -1,46 +0,0 @@
package investigate
import (
"encoding/json"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
)
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)
}
func QueryHotelContacts() []models.HotelContactRequest {
dataStr := fakerGetRequest("query/contacts/hotel/320581199103182689")
var data []models.HotelContactRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}
func QueryRailwayContacts() []models.RailwayContactRequest {
dataStr := fakerGetRequest("query/contacts/railway/320581199103182689")
var data []models.RailwayContactRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}
func QueryPatients() []models.PatientRequest {
dataStr := fakerGetRequest("query/contacts/railway/320581199103182689")
var data []models.PatientRequest
err := json.Unmarshal([]byte(dataStr), &data)
if err != nil {
panic(err)
}
return data
}

View File

@@ -1 +0,0 @@
package investigate

104
service/management/faker.go Normal file
View File

@@ -0,0 +1,104 @@
package management
import (
"encoding/json"
"fmt"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
"strings"
"time"
)
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)
}
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
}
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
}
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
}
func queryContacts(identification string) []models.BackObservation {
hotelContacts := queryHotelContacts(identification)
railwayContacts := queryRailwayContacts(identification)
observations := append(fakerContacts2Observations(hotelContacts), fakerContacts2Observations(railwayContacts)...)
return observations
}
func splitFakerAddress(fakerAddress string) string {
addresses := strings.Split(fakerAddress, " ")
return addresses[1] + " " + addresses[2]
}
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
}

View File

@@ -0,0 +1,22 @@
package management
import (
"nCovTrack-Backend/models"
)
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)
}
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")
}
func DeleteLocation(id int) bool {
ok, rowsAffected := models.DeleteById[models.BackLocation](id)
return (ok && rowsAffected != 0)
}

View File

@@ -0,0 +1,223 @@
package management
import (
"fmt"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"nCovTrack-Backend/utils"
"time"
)
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
if rowsAffected != 0 {
pullSituationRecord(claims, observations)
}
return rowsAffected
}
func ListObservation(jsonMap map[string]interface{}) []models.ListObeservation {
colMap := models.MapJ2c[models.BackObservation](jsonMap, true)
queryMap := []map[string]interface{}{colMap}
return listObservation(queryMap)
}
func GetObservation(id int) map[string]interface{} {
observationCMap := models.GetField[models.BackObservation]([]map[string]interface{}{{"id": id}}, true, "is_delete")
observationJMap := models.MapC2j[models.BackObservation](observationCMap, true)
pcrRecordCMap := models.ListField[models.BackPcr]([]map[string]interface{}{{"observation": id}}, true, "is_delete")
pcrRecordJMap := models.MapsC2j[models.BackPcr](*pcrRecordCMap, true)
situationRecordCMap := models.ListField[models.BackSituationRecord]([]map[string]interface{}{{"observation": id}}, true, "is_delete")
situationRecordJMap := models.MapsC2j[models.BackSituationRecord](*situationRecordCMap, true)
observationJMap["pcrRecord"] = pcrRecordJMap
observationJMap["situationRecord"] = situationRecordJMap
return observationJMap
}
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 {
colMap := models.MapJ2c[models.BackObservation](jsonMap, true)
models.BeforeSave(colMap, claims.ID)
oldMap := models.Get[models.BackObservation]([]map[string]interface{}{{"id": jsonMap["id"]}})
insertSituationRecord(claims, oldMap, colMap)
return false
}
// listObservation the queryMap need with table name
// @Param queryMap the colMap of models.BackObservation
func listObservation(queryMap []map[string]interface{}) []models.ListObeservation {
var observations []models.ListObeservation
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
}
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 := record00_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)
// Notify the admin of pull region
// Send email to the region admin
}
func updateObservationRegion(region string) {
// Add to notify
// Send email to the target region amdin
}
func updateWhileDiagnosis(claims models.TokenClaims, patientId int) {
// Update sub contact to contact and nomeasure
// Add to notify
}
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_20":
recordContent = record00_20()
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 "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))
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 record00_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)
}

44
service/notify/notify.go Normal file
View File

@@ -0,0 +1,44 @@
package notify
import (
"encoding/json"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"strconv"
)
const (
NOTIFY_KEY = "notification_"
NOTIFY_DEL_VALUE = "notify_del"
)
func InsertNotification(userId int, notification models.BackNotification) {
key := NOTIFY_KEY + strconv.Itoa(userId)
valueStr, _ := json.Marshal(notification)
global.Redis.RPush(key, valueStr)
}
func DeleteNotification(userId int, index int) {
key := NOTIFY_KEY + strconv.Itoa(userId)
pipe := global.Redis.TxPipeline()
pipe.LSet(key, int64(index), NOTIFY_DEL_VALUE)
pipe.LRem(key, 2, NOTIFY_DEL_VALUE)
pipe.Exec()
}
func QueryNotificationLen(userId int) int {
key := NOTIFY_KEY + strconv.Itoa(userId)
return int(global.Redis.LLen(key).Val())
}
func ListNotifycation(userId int, start, end int) []models.BackNotification {
key := NOTIFY_KEY + strconv.Itoa(userId)
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
}

View File

@@ -27,11 +27,13 @@ const (
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 = "待确认"
@@ -54,6 +56,22 @@ func cacheNCovStatistics() {
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{}) {

View File

@@ -51,6 +51,32 @@ func GetChinaNCovStatistic() models.ChinaData {
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()

View File

@@ -17,7 +17,6 @@ const (
// Login if login success, will return token
func Login(user map[string]interface{}) (token string) {
// TODO: need to detect is passed or not
account := user["account"].(string)
var queryMap []map[string]interface{}
if strings.Contains(account, "@") {

View File

@@ -2,7 +2,6 @@ package utils
import (
"encoding/json"
"fmt"
"nCovTrack-Backend/global"
"nCovTrack-Backend/models"
"time"
@@ -59,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)