🐛 fix bug

This commit is contained in:
loser 2024-03-21 21:08:45 +08:00
parent 26220f0276
commit 433ff17755
25 changed files with 652 additions and 244 deletions

View File

@ -15,7 +15,8 @@ WORKDIR /go/src/miniapp
COPY --from=0 /go/src/miniapp/server ./ COPY --from=0 /go/src/miniapp/server ./
COPY --from=0 /go/src/miniapp/resource ./resource/ COPY --from=0 /go/src/miniapp/resource ./resource/
COPY --from=0 /go/src/miniapp/config.yaml ./ COPY --from=0 /go/src/miniapp/docker/config.docker.yaml ./docker/
COPY --from=0 /go/src/miniapp/docker ./
EXPOSE 8888 EXPOSE 8888
ENTRYPOINT ./server -c config.yaml ENTRYPOINT ./server -c docker/config.docker.yaml

View File

@ -1,8 +1,11 @@
package app package app
import ( import (
"fmt"
"git.echol.cn/loser/logger/log" "git.echol.cn/loser/logger/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/medivhzhan/weapp/v3"
msg "github.com/medivhzhan/weapp/v3/subscribemessage"
"miniapp/api" "miniapp/api"
"miniapp/model/app" "miniapp/model/app"
"miniapp/model/app/request" "miniapp/model/app/request"
@ -86,6 +89,13 @@ func (u *UserApi) UpdateUser(ctx *gin.Context) {
if p.Phone != "" { if p.Phone != "" {
loginUser.Phone = p.Phone loginUser.Phone = p.Phone
} }
if p.SurgeryTime != "" {
//t, _ := time.Parse("2006-01-02", p.SurgeryTime)
//format := t.UTC().Format("2006-01-02")
loginUser.SurgeryTime = p.SurgeryTime
}
loginUser.IsSurgery = p.IsSurgery
loginUser.HospitalId = p.HospitalId
// 修改数据 // 修改数据
if err := userService.UpdateUserInfo(&loginUser); err != nil { if err := userService.UpdateUserInfo(&loginUser); err != nil {
log.Errorf("修改用户信息失败:%s", err.Error()) log.Errorf("修改用户信息失败:%s", err.Error())
@ -137,3 +147,46 @@ func (u *UserApi) GetInfo(ctx *gin.Context) {
} }
r.OkWithData(userInfo, ctx) r.OkWithData(userInfo, ctx)
} }
// SendMedicineRemind 发送用药提醒消息
func (u *UserApi) SendMedicineRemind(ctx *gin.Context) {
var req utils.SendRequest
if err := ctx.ShouldBind(&req); err != nil {
r.FailWithMessage("参数错误: "+err.Error(), ctx)
return
}
//app-id: wxaaf66dbb5c3983b3
//app-secret: 0abba24dbff43febba1e551651f693b4
//template-id: PgxoZOOSDgBcmIGd_EVLDnYUmL3eu6NQTAZCsHQeuWY
sdk := weapp.NewClient("wxaaf66dbb5c3983b3", "0abba24dbff43febba1e551651f693b4")
msgData := msg.SendRequest{
ToUser: "o9Fq_6_cYKvOWnyUM3McC11hWsTI",
TemplateID: "PgxoZOOSDgBcmIGd_EVLDnYUmL3eu6NQTAZCsHQeuWY",
Page: "page/index/todo",
MiniprogramState: msg.MiniprogramStateTrial,
Data: msg.SendData{
"thing1": msg.SendValue{Value: "眼药水"},
"time2": msg.SendValue{Value: "20:00"},
"short_thing17": msg.SendValue{Value: "一天4次"},
"time15": msg.SendValue{Value: "2024-03-20 20:00"},
},
}
send, err := sdk.NewSubscribeMessage().Send(&msgData)
if err != nil {
return
}
err = send.GetResponseError()
if err != nil {
fmt.Printf("微信返回错误: %#v", err)
return
}
fmt.Printf("返回结果: %#v", send)
r.Ok(ctx)
}

View File

@ -1,203 +0,0 @@
# miniapp Global Configuration
# jwt configuration
jwt:
signing-key: qmPlus
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
# zap logger configuration
zap:
level: info
format: console
prefix: "[miniapp]"
director: log
show-line: true
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
log-in-console: true
# redis configuration
redis:
db: 0
addr: 177.7.0.14:6379
password: ""
# email configuration
email:
to: xxx@qq.com
port: 465
from: xxx@163.com
host: smtp.163.com
is-ssl: true
secret: xxx
nickname: test
# system configuration
system:
env: public # Change to "develop" to skip authentication for development mode
addr: 8888
db-type: mysql
oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server.exe.exe/utils/upload/upload.go 中 NewOss函数配置
use-redis: false # 使用redis
use-multipoint: false
# IP限制次数 一个小时15000次
iplimit-count: 15000
# IP限制一个小时
iplimit-time: 3600
# captcha configuration
captcha:
key-long: 6
img-width: 240
img-height: 80
open-captcha: 0 # 0代表一直开启大于0代表限制次数
open-captcha-timeout: 3600 # open-captcha大于0时才生效
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息如果一定要手动初始化请看https://gin-vue-admin.com/docs/first_master
mysql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
# pgsql connect configuration
# 未初始化之前请勿手动修改数据库信息如果一定要手动初始化请看https://gin-vue-admin.com/docs/first_master
pgsql:
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
db-list:
- disable: true # 是否禁用
type: "" # 数据库的类型,目前支持mysql、pgsql
alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一
path: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
max-idle-conns: 10
max-open-conns: 100
log-mode: ""
log-zap: false
# local configuration
local:
path: uploads/file
store-path: uploads/file
# autocode configuration
autocode:
transfer-restart: true
# root 自动适配项目根目录
# 请不要手动配置,他会在项目加载的时候识别出根路径
root: ""
server: /server.exe.exe
server-plug: /plugin/%s
server-api: /api/v1/%s
server-initialize: /initialize
server-model: /model/%s
server-request: /model/%s/request/
server-router: /router/%s
server-service: /service/%s
web: /web/src
web-api: /api
web-form: /view
web-table: /view
# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
qiniu:
zone: ZoneHuaDong
bucket: ""
img-path: ""
use-https: false
access-key: ""
secret-key: ""
use-cdn-domains: false
# aliyun oss configuration
aliyun-oss:
endpoint: yourEndpoint
access-key-id: yourAccessKeyId
access-key-secret: yourAccessKeySecret
bucket-name: yourBucketName
bucket-url: yourBucketUrl
base-path: yourBasePath
# tencent cos configuration
tencent-cos:
bucket: xxxxx-10005608
region: ap-shanghai
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: miniapp
# aws s3 configuration (minio compatible)
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
endpoint: ""
s3-force-path-style: false
disable-ssl: false
secret-id: your-secret-id
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: miniapp
# huawei obs configuration
hua-wei-obs:
path: you-path
bucket: you-bucket
endpoint: you-endpoint
access-key: you-access-key
secret-key: you-secret-key
# excel configuration
excel:
dir: ./resource/excel/
# timer task db clear table
Timer:
start: true
spec: "@daily" # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3
detail:
- tableName: sys_operation_records
compareField: created_at
interval: 2160h
- tableName: jwt_blacklists
compareField: created_at
interval: 168h
# 跨域配置
# 需要配合 server.exe.exe/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用
cors:
mode: whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝
whitelist:
- allow-origin: example1.com
allow-headers: content-type
allow-methods: GET, POST
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true # 布尔值
- allow-origin: example2.com
allow-headers: content-type
allow-methods: GET, POST
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true # 布尔值

View File

@ -1,12 +1,12 @@
package config package config
// MiniApp 微信小程序配置
type MiniApp struct { type MiniApp struct {
AppId string `mapstructure:"app-id" yaml:"app-id"` AppId string `mapstructure:"app-id" yaml:"app-id"`
AppSecret string `mapstructure:"app-secret" yaml:"app-secret"` AppSecret string `mapstructure:"app-secret" yaml:"app-secret"`
TemplateID string `mapstructure:"template-id" yaml:"template-id"`
} }
// 微信小程序配置
// 微信支付配置 // 微信支付配置
type wechatPayConfig struct { type wechatPayConfig struct {
MchId string `mapstructure:"mchId" yaml:"mchId"` // 商户号 MchId string `mapstructure:"mchId" yaml:"mchId"` // 商户号

View File

@ -79,12 +79,12 @@ mini-app:
app-secret: 0abba24dbff43febba1e551651f693b4 app-secret: 0abba24dbff43febba1e551651f693b4
mysql: mysql:
prefix: "" prefix: ""
port: "3307" port: "3306"
config: charset=utf8mb4&parseTime=True&loc=Local config: charset=utf8mb4&parseTime=True&loc=Local
db-name: mini_app db-name: jmyl
username: root username: jmyl
password: Jmyl123123 password: Jmyl123123
path: 47.116.50.126 path: 101.35.50.11
engine: "" engine: ""
log-mode: error log-mode: error
max-idle-conns: 10 max-idle-conns: 10
@ -92,8 +92,8 @@ mysql:
singular: false singular: false
log-zap: false log-zap: false
redis: redis:
addr: 47.116.50.126:6378 addr: 101.35.50.11:6379
password: "Jmyl123123" password: "loser7659"
db: 0 db: 0
system: system:
env: public env: public

132
docker/config.docker.yaml Normal file
View File

@ -0,0 +1,132 @@
aliyun-oss:
endpoint: oss-cn-chengdu.aliyuncs.com
access-key-id: LTAI5tFHes6HBWJFUjuPwHso
access-key-secret: qXuWtEJvYEQvj9yhkmLYfRxHShheYa
bucket-name: jmyl-app
bucket-url: https://jmyl-app.oss-cn-chengdu.aliyuncs.com
base-path: miniapp
autocode:
server-model: /model/%s
server-router: /router/%s
server: /server.exe.exe
server-api: /api/v1/%s
server-plug: /plugin/%s
server-initialize: /initialize
root: C:\Users\Administrator\Desktop
web-table: /view
web: /web/src
server-service: /service/%s
server-request: /model/%s/request/
web-api: /api
web-form: /view
transfer-restart: true
captcha:
key-long: 4
img-width: 240
img-height: 80
open-captcha: 0
open-captcha-timeout: 3600
cors:
mode: strict-whitelist
whitelist:
- allow-origin: example1.com
allow-methods: POST, GET
allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true
- allow-origin: example2.com
allow-methods: GET, POST
allow-headers: content-type
expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type
allow-credentials: true
db-list:
- type: ""
alias-name: ""
prefix: ""
port: ""
config: ""
db-name: ""
username: ""
password: ""
path: ""
engine: ""
log-mode: ""
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
disable: true
email:
to: xxx@qq.com
from: xxx@163.com
host: smtp.163.com
secret: xxx
nickname: test
port: 465
is-ssl: true
excel:
dir: ./resource/excel/
jwt:
signing-key: f2b1b2af-c8f1-43cf-88e4-40b0a64b5487
expires-time: 7d
buffer-time: 1d
issuer: qmPlus
local:
path: uploads/file
store-path: uploads/file
mini-app:
app-id: wxaaf66dbb5c3983b3
app-secret: 0abba24dbff43febba1e551651f693b4
mysql:
prefix: ""
port: "3307"
config: charset=utf8mb4&parseTime=True&loc=Local
db-name: mini_app
username: root
password: Jmyl123123
path: 47.113.103.195
engine: ""
log-mode: error
max-idle-conns: 10
max-open-conns: 100
singular: false
log-zap: false
redis:
addr: 127.0.0.1:6379
password: "Jmyl123123"
db: 0
system:
env: public
db-type: mysql
oss-type: aliyun-oss
router-prefix: ""
addr: 8888
iplimit-count: 15000
iplimit-time: 3600
use-multipoint: false
use-redis: true
timer:
spec: '@daily'
detail:
- tableName: sys_operation_records
compareField: created_at
interval: 2160h
- tableName: jwt_blacklists
compareField: created_at
interval: 168h
start: true
with_seconds: false
zap:
level: info
prefix: '[miniapp]'
format: console
director: log
encode-level: LowercaseColorLevelEncoder
stacktrace-key: stacktrace
max-age: 0
show-line: true
log-in-console: true

5
go.mod
View File

@ -15,13 +15,14 @@ require (
github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.8.0 github.com/glebarez/sqlite v1.8.0
github.com/go-co-op/gocron v1.13.0
github.com/go-oauth2/oauth2/v4 v4.5.2 github.com/go-oauth2/oauth2/v4 v4.5.2
github.com/go-oauth2/redis/v4 v4.1.1 github.com/go-oauth2/redis/v4 v4.1.1
github.com/go-redis/redis/v8 v8.11.4 github.com/go-redis/redis/v8 v8.11.4
github.com/go-sql-driver/mysql v1.7.1 github.com/go-sql-driver/mysql v1.7.1
github.com/gofrs/uuid/v5 v5.0.0 github.com/gofrs/uuid/v5 v5.0.0
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4 github.com/gookit/color v1.5.4
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible
github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84 github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84
@ -37,7 +38,7 @@ require (
github.com/shirou/gopsutil/v3 v3.23.6 github.com/shirou/gopsutil/v3 v3.23.6
github.com/songzhibin97/gkit v1.2.11 github.com/songzhibin97/gkit v1.2.11
github.com/spf13/viper v1.16.0 github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.9.0
github.com/swaggo/files v1.0.1 github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.2 github.com/swaggo/swag v1.16.2

10
go.sum
View File

@ -137,6 +137,8 @@ github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E= github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc= github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8= github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
github.com/go-co-op/gocron v1.13.0 h1:BjkuNImPy5NuIPEifhWItFG7pYyr27cyjS6BN9w/D4c=
github.com/go-co-op/gocron v1.13.0/go.mod h1:GD5EIEly1YNW+LovFVx5dzbYVcIc8544K99D8UVRpGo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -264,8 +266,9 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
@ -467,8 +470,8 @@ github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -480,8 +483,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=

View File

@ -1,11 +1,10 @@
package initialize package initialize
import ( import (
swaggerFiles "github.com/swaggo/files"
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger" ginSwagger "github.com/swaggo/gin-swagger"
"net/http"
"miniapp/global" "miniapp/global"
"miniapp/middleware" "miniapp/middleware"
@ -31,7 +30,7 @@ func Routers() *gin.Engine {
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址 Router.StaticFS(global.GVA_CONFIG.Local.StorePath, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
// Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.exe.exe.exe.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件") // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.exe.exe.exe.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
// 跨域,如需跨域可以打开下面的注释 // 跨域,如需跨域可以打开下面的注释
// Router.Use(middleware.Cors()) // 直接放行全部跨域请求 Router.Use(middleware.Cors()) // 直接放行全部跨域请求
// Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求 // Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
//global.GVA_LOG.Info("use middleware cors") //global.GVA_LOG.Info("use middleware cors")

View File

@ -4,6 +4,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"miniapp/client" "miniapp/client"
"miniapp/oauth2" "miniapp/oauth2"
"miniapp/task"
"miniapp/core" "miniapp/core"
"miniapp/global" "miniapp/global"
@ -41,5 +42,6 @@ func main() {
client.InitRedisClient() client.InitRedisClient()
oauth2.InitOAuth2Server() // 初始化OAuth2服务 oauth2.InitOAuth2Server() // 初始化OAuth2服务
initialize.InitKp() // 初始化关键词处理器 initialize.InitKp() // 初始化关键词处理器
task.InitTask() // 初始化定时任务
core.RunWindowsServer() core.RunWindowsServer()
} }

View File

@ -24,6 +24,9 @@ type ChangeUserInfo struct {
Nickname string `json:"nickname" form:"nickname"` // 昵称 Nickname string `json:"nickname" form:"nickname"` // 昵称
Avatar string `json:"avatar" form:"avatar"` // 头像 Avatar string `json:"avatar" form:"avatar"` // 头像
Phone string `json:"phone" form:"phone"` // 手机号 Phone string `json:"phone" form:"phone"` // 手机号
IsSurgery int `json:"isSurgery" gorm:"default:0;comment:是否已经手术 0未手术 1已手术"`
HospitalId int `json:"hospitalId" gorm:"comment:手术医院"`
SurgeryTime string `json:"surgery_time" gorm:"comment:手术时间"`
} }
// ChangePassword 修改密码 // ChangePassword 修改密码

View File

@ -34,7 +34,7 @@ type UserVO struct {
IsSurgery int `json:"isSurgery" gorm:"default:0;comment:是否已经手术 0未手术 1已手术"` IsSurgery int `json:"isSurgery" gorm:"default:0;comment:是否已经手术 0未手术 1已手术"`
IsInfo int `json:"isInfo" gorm:"default:0;comment:是否已经填写信息 0未填写 1已填写"` IsInfo int `json:"isInfo" gorm:"default:0;comment:是否已经填写信息 0未填写 1已填写"`
HospitalId int `json:"hospital_id" gorm:"comment:手术医院"` HospitalId int `json:"hospital_id" gorm:"comment:手术医院"`
SurgeryTime time.Time `json:"surgery_time" gorm:"comment:手术时间"` SurgeryTime string `json:"surgery_time" gorm:"comment:手术时间"`
Todos []common.UserTodo `json:"todos" gorm:"comment:用户待办事项"` Todos []common.UserTodo `json:"todos" gorm:"comment:用户待办事项"`
Notes []common.Notes `json:"notes" gorm:"comment:用户须知"` Notes []common.Notes `json:"notes" gorm:"comment:用户须知"`
} }

View File

@ -17,7 +17,7 @@ type User struct {
IsSurgery int `json:"isSurgery" gorm:"default:0;comment:是否已经手术 0未手术 1已手术"` IsSurgery int `json:"isSurgery" gorm:"default:0;comment:是否已经手术 0未手术 1已手术"`
IsInfo int `json:"isInfo" gorm:"default:0;comment:是否已经填写信息 0未填写 1已填写"` IsInfo int `json:"isInfo" gorm:"default:0;comment:是否已经填写信息 0未填写 1已填写"`
HospitalId int `json:"hospital_id" gorm:"comment:手术医院"` HospitalId int `json:"hospital_id" gorm:"comment:手术医院"`
SurgeryTime time.Time `json:"surgery_time" gorm:"comment:手术时间"` SurgeryTime string `json:"surgery_time" gorm:"comment:手术时间"`
LastLoginAt *time.Time `json:"last_login_at" gorm:"comment:'最后登录时间'"` LastLoginAt *time.Time `json:"last_login_at" gorm:"comment:'最后登录时间'"`
} }

View File

@ -4,3 +4,10 @@ type GetUserNotes struct {
UserId string `json:"userId" form:"userId" binding:"required"` UserId string `json:"userId" form:"userId" binding:"required"`
IsFinish string `json:"isFinish" form:"isFinish" binding:"required"` IsFinish string `json:"isFinish" form:"isFinish" binding:"required"`
} }
type TodoTask struct {
OpenId string `json:"openId" form:"openId"`
Context string `json:"context" form:"context"`
Frequency string `json:"frequency" form:"frequency"`
RemindTime string `json:"remindTime" form:"remindTime"`
}

View File

@ -6,6 +6,10 @@ type Todos struct {
// 任务列表 // 任务列表
global.GVA_MODEL global.GVA_MODEL
Content string `json:"content" form:"content" gorm:"comment:任务内容;"` Content string `json:"content" form:"content" gorm:"comment:任务内容;"`
RemindTime string `json:"remindTime" form:"remindTime" gorm:"comment:提醒时间;"`
RemindPeriod int `json:"remindPeriod" form:"remindPeriod" gorm:"comment:0术前1术后;"`
Frequency string `json:"frequency" form:"frequency" gorm:"comment:用药频率;"`
RemindDay string `json:"remindDay" form:"remindDay" gorm:"comment:提醒日期;"`
} }
func (t Todos) TableName() string { func (t Todos) TableName() string {

View File

@ -7,6 +7,10 @@ type UserTodo struct {
UserId int `json:"userId" form:"userId" gorm:"comment:用户id;"` UserId int `json:"userId" form:"userId" gorm:"comment:用户id;"`
Content string `json:"content" form:"content" gorm:"comment:任务内容;"` Content string `json:"content" form:"content" gorm:"comment:任务内容;"`
IsFinish int `json:"isFinish" form:"isFinish" gorm:"comment:是否完成;"` IsFinish int `json:"isFinish" form:"isFinish" gorm:"comment:是否完成;"`
RemindTime string `json:"remindTime" form:"remindTime" gorm:"comment:提醒时间;"`
RemindPeriod int `json:"remindPeriod" form:"remindPeriod" gorm:"comment:术前,术后;"`
Frequency string `json:"frequency" form:"frequency" gorm:"comment:用药频率;"`
RemindDay string `json:"remindDay" form:"remindDay" gorm:"comment:提醒日期;"`
} }
func (u UserTodo) TableName() string { func (u UserTodo) TableName() string {

View File

@ -15,7 +15,7 @@ type SysOperationRecord struct {
Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径 Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径
Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态 Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态
Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟 Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
Agent string `json:"agent" form:"agent" gorm:"column:agent;comment:代理"` // 代理 Agent string `json:"agent" form:"agent" gorm:"column:agent; type:longtext; comment:代理"` // 代理
ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息 ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息
Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body
Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body

View File

@ -116,6 +116,9 @@ func ExtensionFields(ti oauth2.TokenInfo) (fieldsValue map[string]any) {
fieldsValue["IsSurgery"] = userInfo.IsSurgery fieldsValue["IsSurgery"] = userInfo.IsSurgery
fieldsValue["HospitalId"] = userInfo.HospitalId fieldsValue["HospitalId"] = userInfo.HospitalId
fieldsValue["IsInfo"] = userInfo.IsInfo fieldsValue["IsInfo"] = userInfo.IsInfo
fieldsValue["SurgeryTime"] = userInfo.SurgeryTime
fieldsValue["HospitalId"] = userInfo.HospitalId
fieldsValue["IsSurgery"] = userInfo.IsSurgery
return return
} }

View File

@ -25,6 +25,7 @@ func (s *LoginRouter) InitUserRouter(Router *gin.RouterGroup) {
userRouter.GET("todo", todoApi.GetUserTodos) userRouter.GET("todo", todoApi.GetUserTodos)
userRouter.PUT("todo", todoApi.UpdateTodo) userRouter.PUT("todo", todoApi.UpdateTodo)
userRouter.POST("msg", baseApi.SendMedicineRemind)
} }
} }

View File

@ -10,6 +10,7 @@ import (
"miniapp/model/app/request" "miniapp/model/app/request"
"miniapp/model/app/response" "miniapp/model/app/response"
"miniapp/model/common" "miniapp/model/common"
"strings"
"time" "time"
) )
@ -49,7 +50,53 @@ func (UserService) CheckUnionIdIsExist(unionId, openId string) bool {
// UpdateUserInfo 更新普通用户信息 // UpdateUserInfo 更新普通用户信息
func (UserService) UpdateUserInfo(e *app.User) (err error) { func (UserService) UpdateUserInfo(e *app.User) (err error) {
return global.GVA_DB.Updates(&e).Error tUser := app.User{}
err = global.GVA_DB.Where("id = ?", e.ID).First(&tUser).Error
err = global.GVA_DB.Updates(&e).Error
if err != nil {
global.GVA_LOG.Error("更新用户信息失败", zap.Error(err))
return
}
if e.SurgeryTime != "" {
if tUser.HospitalId != e.HospitalId {
err = global.GVA_DB.Delete(&common.UserTodo{}, "user_id = ?", e.ID).Error
if err != nil {
global.GVA_LOG.Error("清除用户Todo列表失败", zap.Error(err))
return
}
var hospital common.Hospital
err = global.GVA_DB.Where("id = ?", e.HospitalId).Preload("Todos").First(&hospital).Error
if err != nil {
global.GVA_LOG.Error("获取医院信息失败", zap.Error(err))
return
}
if len(hospital.Todos) != 0 {
var userTodos []common.UserTodo
for _, todo := range hospital.Todos {
for _, s := range strings.Split(todo.RemindTime, ",") {
userTodos = append(userTodos, common.UserTodo{
Content: todo.Content,
UserId: int(e.ID),
IsFinish: 0,
RemindTime: s,
RemindPeriod: todo.RemindPeriod,
})
}
}
err = global.GVA_DB.Create(&userTodos).Error
if err != nil {
global.GVA_LOG.Error("创建用户Todo列表失败", zap.Error(err))
return
}
}
}
}
return err
} }
// GetAverageUserList 查询普通用户列表 // GetAverageUserList 查询普通用户列表
@ -126,12 +173,16 @@ func (UserService) UpdateUserHospital(r *request.ChangeUserHospital) (err error)
if len(hospital.Todos) != 0 { if len(hospital.Todos) != 0 {
var userTodos []common.UserTodo var userTodos []common.UserTodo
for _, todo := range hospital.Todos { for _, todo := range hospital.Todos {
for _, s := range strings.Split(todo.RemindTime, ",") {
userTodos = append(userTodos, common.UserTodo{ userTodos = append(userTodos, common.UserTodo{
Content: todo.Content, Content: todo.Content,
UserId: r.UserId, UserId: r.UserId,
IsFinish: 0, IsFinish: 0,
RemindTime: s,
RemindPeriod: todo.RemindPeriod,
}) })
} }
}
err = global.GVA_DB.Create(&userTodos).Error err = global.GVA_DB.Create(&userTodos).Error
if err != nil { if err != nil {
global.GVA_LOG.Error("创建用户Todo列表失败", zap.Error(err)) global.GVA_LOG.Error("创建用户Todo列表失败", zap.Error(err))
@ -139,9 +190,9 @@ func (UserService) UpdateUserHospital(r *request.ChangeUserHospital) (err error)
} }
} }
t, _ := time.Parse("2006-01-02", r.SurgeryTime) //t, _ := time.Parse("2006-01-02", r.SurgeryTime)
err = global.GVA_DB.Table("t_user").Where("id = ?", r.UserId).Updates(app.User{IsInfo: 1, HospitalId: r.HospitalId, SurgeryTime: t}).Error err = global.GVA_DB.Table("t_user").Where("id = ?", r.UserId).Updates(app.User{IsInfo: 1, HospitalId: r.HospitalId, SurgeryTime: r.SurgeryTime}).Error
if err != nil { if err != nil {
global.GVA_LOG.Error("更新用户信息失败", zap.Error(err)) global.GVA_LOG.Error("更新用户信息失败", zap.Error(err))
return return

View File

@ -23,12 +23,48 @@ func (h HospitalService) CreateHospital(hospital *common.Hospital) (err error) {
// UpdateHospital 更新医院 // UpdateHospital 更新医院
func (h HospitalService) UpdateHospital(hospital *common.Hospital) (err error) { func (h HospitalService) UpdateHospital(hospital *common.Hospital) (err error) {
return global.GVA_DB.Updates(&hospital).Error for i := range hospital.Notes {
if hospital.Notes[i].NotesTime == "手术前" {
hospital.Notes[i].NotestimeNum = 1
} else if hospital.Notes[i].NotesTime == "手术中" {
hospital.Notes[i].NotestimeNum = 2
} else {
hospital.Notes[i].NotestimeNum = 3
}
}
err = global.GVA_DB.Model(&common.Notes{}).Save(&hospital.Notes).Error
if err != nil {
global.GVA_LOG.Error("更新医院注意事项失败", zap.Error(err))
return
}
err = global.GVA_DB.Model(&hospital).Association("Notes").Replace(&hospital.Notes)
if err != nil {
global.GVA_LOG.Error("更新医院注意事项失败", zap.Error(err))
return
}
err = global.GVA_DB.Model(&common.Todos{}).Save(&hospital.Todos).Error
if err != nil {
global.GVA_LOG.Error("更新医院Todo失败", zap.Error(err))
return
}
err = global.GVA_DB.Model(&hospital).Association("Todos").Replace(&hospital.Todos)
if err != nil {
global.GVA_LOG.Error("更新医院Todo关联失败", zap.Error(err))
return
}
return global.GVA_DB.Save(&hospital).Error
} }
// DeleteHospital 删除医院 // DeleteHospital 删除医院
func (h HospitalService) DeleteHospital(hospital *common.Hospital) (err error) { func (h HospitalService) DeleteHospital(hospital *common.Hospital) (err error) {
return global.GVA_DB.Select("Notes").Delete(&hospital).Error err = global.GVA_DB.Model(&hospital).Association("Notes").Clear()
err = global.GVA_DB.Model(&hospital).Association("Todos").Clear()
if err != nil {
return err
}
return global.GVA_DB.Delete(&hospital).Error
} }
// GetHospitalById 根据id获取医院 // GetHospitalById 根据id获取医院

18
task/task.go Normal file
View File

@ -0,0 +1,18 @@
package task
import (
"github.com/go-co-op/gocron"
"time"
)
var WxTask *gocron.Scheduler
func InitTask() {
WxTask = gocron.NewScheduler(time.Local)
// 每天凌晨1点执行
_, _ = WxTask.Every(1).Day().At("01:00").Do(CheckUserSurgeryDate) // 检查用户是否已到手术日期
// 开启定时任务
WxTask.StartAsync()
}

19
task/user.go Normal file
View File

@ -0,0 +1,19 @@
package task
import (
"miniapp/global"
"miniapp/model/app"
"time"
)
// CheckUserSurgeryDate 检查用户是否已到手术日期
func CheckUserSurgeryDate() {
var users []app.User
global.GVA_DB.Model(&app.User{}).Find(&users)
for _, user := range users {
parse, _ := time.Parse("2006-01-02", user.SurgeryTime)
if time.Now().Sub(parse).Hours()/24 == 0 {
global.GVA_DB.Model(&app.User{}).Where("id = ?", user.ID).Updates(app.User{IsSurgery: 1})
}
}
}

83
task/wxMsg.go Normal file
View File

@ -0,0 +1,83 @@
package task
import (
"fmt"
"github.com/medivhzhan/weapp/v3"
msg "github.com/medivhzhan/weapp/v3/subscribemessage"
"go.uber.org/zap"
"miniapp/global"
"miniapp/model/app"
"miniapp/model/common"
"strconv"
"strings"
"time"
)
func SendMsg(userId int) {
var user app.User
err := global.GVA_DB.Model(&user).Where("id = ?", userId).Find(&user).Error
if err != nil {
global.GVA_LOG.Error("获取用户信息失败:%s", zap.Error(err))
return
}
// 获取用户待办列表
var userTodo []common.UserTodo
if user.IsSurgery == 0 {
err = global.GVA_DB.Model(&userTodo).Where("user_id = ? and is_finish = ? and remind_period = 0", userId, 0).Find(&userTodo).Error
if err != nil {
global.GVA_LOG.Error("获取用户待办列表:%s", zap.Error(err))
return
}
} else {
err = global.GVA_DB.Model(&userTodo).Where("user_id = ? and is_finish = ? and remind_period = 1", userId, 0).Find(&userTodo).Error
if err != nil {
global.GVA_LOG.Error("获取用户待办列表:%s", zap.Error(err))
return
}
}
// 根据用户手术信息 发送提醒消息
parse, _ := time.Parse("2006-01-02", user.SurgeryTime)
if time.Now().Sub(parse).Hours()/24 <= -3 {
for _, todo := range userTodo {
jbo, _ := WxTask.Every(1).Day().At(todo.RemindTime).Do(MiniappSendMsg, *user.WechatOpenId, todo.Content, todo.RemindPeriod, todo.RemindTime) // 检查用户待办事项
s := strings.Split("一天3次", "")[2]
// 将s转为int类型
atoi, _ := strconv.Atoi(s)
if jbo.RunCount() == atoi {
WxTask.Remove(jbo)
}
}
}
}
func MiniappSendMsg(openId string, context string, frequency string, remindTime string) {
sdk := weapp.NewClient(global.GVA_CONFIG.MiniApp.AppId, global.GVA_CONFIG.MiniApp.AppSecret)
msgData := msg.SendRequest{
ToUser: openId,
TemplateID: "PgxoZOOSDgBcmIGd_EVLDnYUmL3eu6NQTAZCsHQeuWY",
Page: "/page/index/todo",
MiniprogramState: msg.MiniprogramStateTrial,
Data: msg.SendData{
"thing1": msg.SendValue{Value: context},
"time2": msg.SendValue{Value: remindTime},
"short_thing17": msg.SendValue{Value: frequency},
"time15": msg.SendValue{Value: time.DateTime},
},
}
send, err := sdk.NewSubscribeMessage().Send(&msgData)
if err != nil {
return
}
err = send.GetResponseError()
if err != nil {
fmt.Printf("微信返回错误: %#v", err)
return
}
fmt.Printf("返回结果: %#v", send)
}

190
utils/wx_msg.go Normal file
View File

@ -0,0 +1,190 @@
package utils
import (
"crypto/sha1"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"miniapp/global"
"net/http"
"net/url"
"sort"
"strings"
"sync"
"time"
)
//type wxMsg struct{}
//
//func WxMsgUtils() *wxMsg {
// return &wxMsg{}
//}
type Wechat struct {
appID string
secret string
templateID string
accessToken *AccessToken
sync.Mutex
}
type OpenIDResponse struct {
Openid string `json:"openid"`
SessionKey string `json:"session_key"`
Unionid string `json:"unionid"`
WxErr
}
type CheckTokenRequest struct {
Signature string `json:"signature"`
Timestamp string `json:"timestamp"`
Nonce string `json:"nonce"`
Echostr string `json:"echostr"`
}
type WxErr struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
type AccessToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int64 `json:"expires_in"`
WxErr
}
type SendRequest struct {
Touser string `json:"touser"`
Page string `json:"page"`
Data Message `json:"data"`
EmphasisKeyword string `json:"emphasis_keyword"`
}
type Message map[string]interface{}
func (w *Wechat) SendMsg(req SendRequest) (err error) {
token, err := w.GetAccessToken()
if err != nil {
return
}
api, err := TokenAPI("https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send", token)
if err != nil {
return
}
for key := range req.Data {
req.Data[key] = Message{"value": req.Data[key]}
}
body := map[string]interface{}{
"touser": "o9Fq_6_cYKvOWnyUM3McC11hWsTI",
"template_id": global.GVA_CONFIG.MiniApp.TemplateID,
"page": "/pages/index/todo",
"data": req.Data,
"emphasis_keyword": req.EmphasisKeyword,
}
b, err := json.Marshal(body)
if err != nil {
return err
}
res, err := http.Post(api, "application/json", strings.NewReader(string(b)))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
err = errors.New("WECHAT_SERVER_ERROR")
return err
}
var resp WxErr
if err = json.NewDecoder(res.Body).Decode(&resp); err != nil {
return err
}
if resp.Errcode != 0 {
return errors.New(resp.Errmsg)
}
return nil
}
func (w *Wechat) GetAccessToken() (token string, err error) {
w.Lock()
defer w.Unlock()
if w.accessToken == nil || w.accessToken.ExpiresIn < time.Now().Unix() {
for i := 0; i < 3; i++ {
err = w.getAccessToken()
if err == nil {
break
}
time.Sleep(time.Second)
}
if err != nil {
return
}
}
token = w.accessToken.AccessToken
return
}
func (w *Wechat) CheckSignature(req CheckTokenRequest) (err error) {
if sig := w.sortSha1(req.Timestamp, req.Nonce, req.Echostr); sig != req.Signature {
err = errors.New("check signature failed.")
return
}
return
}
func TokenAPI(api, token string) (string, error) {
u, err := url.Parse(api)
if err != nil {
return "", err
}
query := u.Query()
query.Set("access_token", token)
u.RawQuery = query.Encode()
return u.String(), nil
}
func (w *Wechat) getAccessToken() (err error) {
urls, err := url.Parse("https://api.weixin.qq.com/cgi-bin/token")
if err != nil {
return
}
query := urls.Query()
query.Set("appid", w.appID)
query.Set("secret", w.secret)
query.Set("grant_type", "client_credential")
urls.RawQuery = query.Encode()
res, err := http.Get(urls.String())
if err != nil {
return
}
defer res.Body.Close()
if res.StatusCode != 200 {
return errors.New("wechat internal server error.")
}
var token AccessToken
if err = json.NewDecoder(res.Body).Decode(&token); err != nil {
return
}
if token.Errcode != 0 {
return errors.New(token.Errmsg)
}
w.accessToken.AccessToken = token.AccessToken
w.accessToken.ExpiresIn = token.ExpiresIn
return
}
func (w *Wechat) sortSha1(s ...string) string {
sort.Strings(s)
h := sha1.New()
h.Write([]byte(strings.Join(s, "")))
return fmt.Sprintf("%x", h.Sum(nil))
}