From 4af8640e5fa0eb4a2e727bb7fef87758a8ae70f0 Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Wed, 7 Aug 2024 14:41:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=82=9D=E7=97=85=E7=AE=97=E4=B8=80=E7=AE=97?= =?UTF-8?q?=E5=90=8E=E5=8F=B0api=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- api/controller/Base.go | 16 + api/controller/BaseAgreement.go | 197 ++++++ api/controller/BaseClass.go | 307 ++++++++++ api/controller/Coupon.go | 348 +++++++++++ api/controller/OrderMember.go | 219 +++++++ api/controller/OrderSingle.go | 274 +++++++++ api/controller/Public.go | 328 ++++++++++ api/controller/Question.go | 565 ++++++++++++++++++ api/controller/SystemMember.go | 259 ++++++++ api/controller/SystemSingle.go | 170 ++++++ api/controller/User.go | 178 ++++++ api/controller/UserCoupon.go | 109 ++++ api/dao/AdminUser.go | 108 ++++ api/dao/BaseAgreement.go | 130 ++++ api/dao/BaseClass.go | 180 ++++++ api/dao/Coupon.go | 174 ++++++ api/dao/OrderMember.go | 226 +++++++ api/dao/OrderMemberCoupon.go | 117 ++++ api/dao/OrderMemberRefund.go | 108 ++++ api/dao/OrderSingle.go | 236 ++++++++ api/dao/OrderSingleCoupon.go | 117 ++++ api/dao/OrderSingleRefund.go | 108 ++++ api/dao/Question.go | 423 +++++++++++++ api/dao/QuestionClass.go | 125 ++++ api/dao/SystemMember.go | 123 ++++ api/dao/SystemSingle.go | 123 ++++ api/dao/User.go | 223 +++++++ api/dao/UserCollection.go | 108 ++++ api/dao/UserCoupon.go | 181 ++++++ api/dao/UserInfo.go | 108 ++++ api/dto/BaseAgreement.go | 49 ++ api/dto/BaseClass.go | 59 ++ api/dto/Coupon.go | 101 ++++ api/dto/Login.go | 68 +++ api/dto/OrderMember.go | 126 ++++ api/dto/OrderSingle.go | 139 +++++ api/dto/Public.go | 49 ++ api/dto/Question.go | 187 ++++++ api/dto/SystemMember.go | 55 ++ api/dto/SystemSingle.go | 49 ++ api/dto/User.go | 96 +++ api/dto/UserCollection.go | 62 ++ api/dto/UserCoupon.go | 79 +++ api/exception/exception.go | 43 ++ api/middlewares/auth.go | 57 ++ api/middlewares/cors.go | 29 + api/middlewares/jwt.go | 72 +++ api/middlewares/logrus.go | 60 ++ api/middlewares/requestParamsMiddleware.go | 92 +++ api/model/AdminUser.go | 42 ++ api/model/BaseAgreement.go | 33 + api/model/BaseClass.go | 36 ++ api/model/Coupon.go | 47 ++ api/model/OrderMember.go | 50 ++ api/model/OrderMemberCoupon.go | 35 ++ api/model/OrderMemberRefund.go | 39 ++ api/model/OrderSingle.go | 50 ++ api/model/OrderSingleCoupon.go | 35 ++ api/model/OrderSingleRefund.go | 40 ++ api/model/Question.go | 45 ++ api/model/QuestionClass.go | 33 + api/model/SystemMember.go | 35 ++ api/model/SystemSingle.go | 33 + api/model/User.go | 48 ++ api/model/UserCollection.go | 34 ++ api/model/UserCoupon.go | 38 ++ api/model/UserInfo.go | 43 ++ api/model/model.go | 87 +++ api/requests/BaseAgreement.go | 25 + api/requests/BaseClass.go | 56 ++ api/requests/Coupon.go | 43 ++ api/requests/OrderMember.go | 28 + api/requests/OrderSingle.go | 28 + api/requests/Public.go | 21 + api/requests/Question.go | 112 ++++ api/requests/SystemMember.go | 22 + api/requests/SystemSingle.go | 18 + api/requests/User.go | 36 ++ api/requests/UserCoupon.go | 15 + api/requests/base.go | 4 + api/responses/responses.go | 52 ++ api/router/router.go | 290 +++++++++ api/service/BaseClass.go | 4 + api/service/OrderMember.go | 185 ++++++ api/service/OrderSingle.go | 410 +++++++++++++ api/service/Public.go | 85 +++ api/service/Question.go | 230 +++++++ api/service/SystemMember.go | 17 + api/service/User.go | 107 ++++ api/service/UserCollection.go | 91 +++ api/service/UserCoupon.go | 208 +++++++ config.yaml | 69 +++ config/amqp.go | 9 + config/config.go | 17 + config/dysms.go | 6 + config/jwt.go | 7 + config/log.go | 6 + config/mysql.go | 12 + config/oss.go | 9 + config/redis.go | 9 + config/wechat.go | 20 + consts/http.go | 24 + core/cron.go | 15 + core/logrus.go | 46 ++ core/mysql.go | 56 ++ core/rabbitMq.go | 45 ++ core/redis.go | 25 + core/snowflake.go | 21 + core/validator.go | 95 +++ core/viper.go | 41 ++ extend/aliyun/dysms.go | 104 ++++ extend/aliyun/oss.go | 186 ++++++ extend/rabbitMq/rabbitMq.go | 284 +++++++++ extend/weChat/base.go | 105 ++++ .../weChat/certs/1281030301/apiclient_key.pem | 28 + .../certs/1636644248/apiclient_cert.p12 | Bin 0 -> 2774 bytes .../certs/1636644248/apiclient_cert.pem | 25 + .../weChat/certs/1636644248/apiclient_key.pem | 28 + ...2FCCD1B9ECC8292703AB7363C73D74B6AFDC1A.pem | 24 + .../certs/1659662936/apiclient_cert.p12 | Bin 0 -> 2790 bytes .../certs/1659662936/apiclient_cert.pem | 25 + .../weChat/certs/1659662936/apiclient_key.pem | 28 + ...5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem | 24 + extend/weChat/close.go | 62 ++ extend/weChat/parseNotify.go | 47 ++ extend/weChat/prepay.go | 132 ++++ extend/weChat/refund.go | 46 ++ extend/weChat/userInfo.go | 76 +++ extend/weChat/webAccessToken.go | 68 +++ global/global.go | 24 + go.mod | 100 ++++ go.sum | 332 ++++++++++ main.go | 60 ++ utils/aes.go | 90 +++ utils/captcha.go | 51 ++ utils/compute.go | 85 +++ utils/directory.go | 21 + utils/export.go | 298 +++++++++ utils/idCard.go | 124 ++++ utils/intToString.go | 29 + utils/jwt.go | 40 ++ utils/logger.go | 28 + utils/mask.go | 89 +++ utils/regular.go | 12 + utils/replace.go | 22 + utils/validator.go | 15 + 147 files changed, 13499 insertions(+), 1 deletion(-) create mode 100644 api/controller/Base.go create mode 100644 api/controller/BaseAgreement.go create mode 100644 api/controller/BaseClass.go create mode 100644 api/controller/Coupon.go create mode 100644 api/controller/OrderMember.go create mode 100644 api/controller/OrderSingle.go create mode 100644 api/controller/Public.go create mode 100644 api/controller/Question.go create mode 100644 api/controller/SystemMember.go create mode 100644 api/controller/SystemSingle.go create mode 100644 api/controller/User.go create mode 100644 api/controller/UserCoupon.go create mode 100644 api/dao/AdminUser.go create mode 100644 api/dao/BaseAgreement.go create mode 100644 api/dao/BaseClass.go create mode 100644 api/dao/Coupon.go create mode 100644 api/dao/OrderMember.go create mode 100644 api/dao/OrderMemberCoupon.go create mode 100644 api/dao/OrderMemberRefund.go create mode 100644 api/dao/OrderSingle.go create mode 100644 api/dao/OrderSingleCoupon.go create mode 100644 api/dao/OrderSingleRefund.go create mode 100644 api/dao/Question.go create mode 100644 api/dao/QuestionClass.go create mode 100644 api/dao/SystemMember.go create mode 100644 api/dao/SystemSingle.go create mode 100644 api/dao/User.go create mode 100644 api/dao/UserCollection.go create mode 100644 api/dao/UserCoupon.go create mode 100644 api/dao/UserInfo.go create mode 100644 api/dto/BaseAgreement.go create mode 100644 api/dto/BaseClass.go create mode 100644 api/dto/Coupon.go create mode 100644 api/dto/Login.go create mode 100644 api/dto/OrderMember.go create mode 100644 api/dto/OrderSingle.go create mode 100644 api/dto/Public.go create mode 100644 api/dto/Question.go create mode 100644 api/dto/SystemMember.go create mode 100644 api/dto/SystemSingle.go create mode 100644 api/dto/User.go create mode 100644 api/dto/UserCollection.go create mode 100644 api/dto/UserCoupon.go create mode 100644 api/exception/exception.go create mode 100644 api/middlewares/auth.go create mode 100644 api/middlewares/cors.go create mode 100644 api/middlewares/jwt.go create mode 100644 api/middlewares/logrus.go create mode 100644 api/middlewares/requestParamsMiddleware.go create mode 100644 api/model/AdminUser.go create mode 100644 api/model/BaseAgreement.go create mode 100644 api/model/BaseClass.go create mode 100644 api/model/Coupon.go create mode 100644 api/model/OrderMember.go create mode 100644 api/model/OrderMemberCoupon.go create mode 100644 api/model/OrderMemberRefund.go create mode 100644 api/model/OrderSingle.go create mode 100644 api/model/OrderSingleCoupon.go create mode 100644 api/model/OrderSingleRefund.go create mode 100644 api/model/Question.go create mode 100644 api/model/QuestionClass.go create mode 100644 api/model/SystemMember.go create mode 100644 api/model/SystemSingle.go create mode 100644 api/model/User.go create mode 100644 api/model/UserCollection.go create mode 100644 api/model/UserCoupon.go create mode 100644 api/model/UserInfo.go create mode 100644 api/model/model.go create mode 100644 api/requests/BaseAgreement.go create mode 100644 api/requests/BaseClass.go create mode 100644 api/requests/Coupon.go create mode 100644 api/requests/OrderMember.go create mode 100644 api/requests/OrderSingle.go create mode 100644 api/requests/Public.go create mode 100644 api/requests/Question.go create mode 100644 api/requests/SystemMember.go create mode 100644 api/requests/SystemSingle.go create mode 100644 api/requests/User.go create mode 100644 api/requests/UserCoupon.go create mode 100644 api/requests/base.go create mode 100644 api/responses/responses.go create mode 100644 api/router/router.go create mode 100644 api/service/BaseClass.go create mode 100644 api/service/OrderMember.go create mode 100644 api/service/OrderSingle.go create mode 100644 api/service/Public.go create mode 100644 api/service/Question.go create mode 100644 api/service/SystemMember.go create mode 100644 api/service/User.go create mode 100644 api/service/UserCollection.go create mode 100644 api/service/UserCoupon.go create mode 100644 config.yaml create mode 100644 config/amqp.go create mode 100644 config/config.go create mode 100644 config/dysms.go create mode 100644 config/jwt.go create mode 100644 config/log.go create mode 100644 config/mysql.go create mode 100644 config/oss.go create mode 100644 config/redis.go create mode 100644 config/wechat.go create mode 100644 consts/http.go create mode 100644 core/cron.go create mode 100644 core/logrus.go create mode 100644 core/mysql.go create mode 100644 core/rabbitMq.go create mode 100644 core/redis.go create mode 100644 core/snowflake.go create mode 100644 core/validator.go create mode 100644 core/viper.go create mode 100644 extend/aliyun/dysms.go create mode 100644 extend/aliyun/oss.go create mode 100644 extend/rabbitMq/rabbitMq.go create mode 100644 extend/weChat/base.go create mode 100644 extend/weChat/certs/1281030301/apiclient_key.pem create mode 100644 extend/weChat/certs/1636644248/apiclient_cert.p12 create mode 100644 extend/weChat/certs/1636644248/apiclient_cert.pem create mode 100644 extend/weChat/certs/1636644248/apiclient_key.pem create mode 100644 extend/weChat/certs/1636644248/wechatpay_112FCCD1B9ECC8292703AB7363C73D74B6AFDC1A.pem create mode 100644 extend/weChat/certs/1659662936/apiclient_cert.p12 create mode 100644 extend/weChat/certs/1659662936/apiclient_cert.pem create mode 100644 extend/weChat/certs/1659662936/apiclient_key.pem create mode 100644 extend/weChat/certs/1659662936/wechatpay_5B5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem create mode 100644 extend/weChat/close.go create mode 100644 extend/weChat/parseNotify.go create mode 100644 extend/weChat/prepay.go create mode 100644 extend/weChat/refund.go create mode 100644 extend/weChat/userInfo.go create mode 100644 extend/weChat/webAccessToken.go create mode 100644 global/global.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 utils/aes.go create mode 100644 utils/captcha.go create mode 100644 utils/compute.go create mode 100644 utils/directory.go create mode 100644 utils/export.go create mode 100644 utils/idCard.go create mode 100644 utils/intToString.go create mode 100644 utils/jwt.go create mode 100644 utils/logger.go create mode 100644 utils/mask.go create mode 100644 utils/regular.go create mode 100644 utils/replace.go create mode 100644 utils/validator.go diff --git a/.gitignore b/.gitignore index f4d432a..83e4288 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,11 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +*.log # Dependency directories (remove the comment below to include it) # vendor/ - +.idea/ +.git/ +.DS_Store/ +.tmp/ \ No newline at end of file diff --git a/api/controller/Base.go b/api/controller/Base.go new file mode 100644 index 0000000..283c480 --- /dev/null +++ b/api/controller/Base.go @@ -0,0 +1,16 @@ +package controller + +// Api api接口 +type Api struct { + Public // 公共 + OrderMember // 会员订单 + OrderSingle // 单项订单 + User // 用户 + Coupon // 系统优惠卷 + UserCoupon // 用户优惠卷 + BaseClass // 基础数据-分类 + Question // 问题 + SystemMember // 会员配置 + SystemSingle // 单项配置 + BaseAgreement // 基础数据-协议 +} diff --git a/api/controller/BaseAgreement.go b/api/controller/BaseAgreement.go new file mode 100644 index 0000000..77aae92 --- /dev/null +++ b/api/controller/BaseAgreement.go @@ -0,0 +1,197 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type BaseAgreement struct{} + +// GetBaseAgreementPage 获取协议列表-分页 +func (b *BaseAgreement) GetBaseAgreementPage(c *gin.Context) { + baseAgreementRequest := requests.BaseAgreementRequest{} + req := baseAgreementRequest.GetBaseAgreementPage + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + // 获取分类数据 + baseAgreementDao := dao.BaseAgreementDao{} + baseAgreement, total, err := baseAgreementDao.GetBaseAgreementPageSearch(req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetBaseAgreementListDto(baseAgreement) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetBaseAgreement 获取协议详情 +func (b *BaseAgreement) GetBaseAgreement(c *gin.Context) { + id := c.Param("agreement_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + agreementId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取协议数据 + baseAgreementDao := dao.BaseAgreementDao{} + baseAgreement, err := baseAgreementDao.GetBaseAgreementById(agreementId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 处理返回值 + g := dto.GetBaseAgreementDto(baseAgreement) + + responses.OkWithData(g, c) +} + +// PutBaseAgreement 修改协议 +func (b *BaseAgreement) PutBaseAgreement(c *gin.Context) { + BaseAgreementRequest := requests.BaseAgreementRequest{} + req := BaseAgreementRequest.PutBaseAgreement + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("agreement_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + agreementId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取协议数据 + baseAgreementDao := dao.BaseAgreementDao{} + baseAgreement, err := baseAgreementDao.GetBaseAgreementById(agreementId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 修改值 + baseAgreementData := make(map[string]interface{}) + + // 协议标题 + if req.AgreementTitle != baseAgreement.AgreementTitle { + baseAgreementData["agreement_title"] = req.AgreementTitle + } + + // 协议内容 + if req.AgreementContent != baseAgreement.AgreementContent { + baseAgreementData["agreement_content"] = req.AgreementContent + } + + if len(baseAgreementData) > 0 { + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + err = baseAgreementDao.EditBaseAgreementById(tx, agreementId, baseAgreementData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + } + + responses.Ok(c) +} + +// AddBaseAgreement 新增协议 +func (b *BaseAgreement) AddBaseAgreement(c *gin.Context) { + BaseAgreementRequest := requests.BaseAgreementRequest{} + req := BaseAgreementRequest.AddBaseAgreement + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + baseAgreement := &model.BaseAgreement{ + AgreementTitle: req.AgreementTitle, + AgreementContent: req.AgreementContent, + } + + baseAgreementDao := dao.BaseAgreementDao{} + baseAgreement, err := baseAgreementDao.AddBaseAgreement(tx, baseAgreement) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/BaseClass.go b/api/controller/BaseClass.go new file mode 100644 index 0000000..ca25660 --- /dev/null +++ b/api/controller/BaseClass.go @@ -0,0 +1,307 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type BaseClass struct{} + +// GetBaseClassPage 获取基础分类列表-分页 +func (b *BaseClass) GetBaseClassPage(c *gin.Context) { + baseClassRequest := requests.BaseClassRequest{} + req := baseClassRequest.GetBaseClassPage + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + // 获取分类数据 + baseClassDao := dao.BaseClassDao{} + baseClass, total, err := baseClassDao.GetBaseClassPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetBaseClassListDto(baseClass) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetBaseClassList 获取基础分类列表 +func (b *BaseClass) GetBaseClassList(c *gin.Context) { + baseClassRequest := requests.BaseClassRequest{} + req := baseClassRequest.GetBaseClassList + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 获取分类数据 + baseClassDao := dao.BaseClassDao{} + baseClass, err := baseClassDao.GetBaseClassListSearch(req) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetBaseClassListDto(baseClass) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + responses.OkWithData(g, c) +} + +// PutBaseClassStatus 操作基础分类状态 +func (b *BaseClass) PutBaseClassStatus(c *gin.Context) { + baseClassRequest := requests.BaseClassRequest{} + req := baseClassRequest.PutBaseClassStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("class_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + classId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取基础分类数据 + baseClassDao := dao.BaseClassDao{} + baseClass, err := baseClassDao.GetBaseClassById(classId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 检测状态 + if baseClass.ClassStatus == req.ClassStatus { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + baseClassData := make(map[string]interface{}) + baseClassData["class_status"] = req.ClassStatus + err = baseClassDao.EditBaseClassById(tx, classId, baseClassData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// GetBaseClass 获取基础分类详情 +func (b *BaseClass) GetBaseClass(c *gin.Context) { + id := c.Param("class_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + classId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取基础分类数据 + baseClassDao := dao.BaseClassDao{} + baseClass, err := baseClassDao.GetBaseClassById(classId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 处理返回值 + g := dto.GetBaseClassDto(baseClass) + + responses.OkWithData(g, c) +} + +// PutBaseClass 修改基础分类 +func (b *BaseClass) PutBaseClass(c *gin.Context) { + baseClassRequest := requests.BaseClassRequest{} + req := baseClassRequest.PutBaseClass + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("class_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + classId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取基础分类数据 + baseClassDao := dao.BaseClassDao{} + baseClass, err := baseClassDao.GetBaseClassById(classId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 修改值 + baseClassData := make(map[string]interface{}) + + // 分类名称 + if req.ClassName != baseClass.ClassName { + baseClassData["class_name"] = req.ClassName + } + + // 图标地址 + classIcon := utils.RemoveOssDomain(req.ClassIcon) + if classIcon != baseClass.ClassIcon { + baseClassData["class_icon"] = classIcon + } + + // 分类简介 + if req.ClassBrief != baseClass.ClassBrief { + baseClassData["class_brief"] = req.ClassBrief + } + + // 排序值 + if req.Sort != baseClass.Sort { + baseClassData["sort"] = req.Sort + } + + if len(baseClassData) > 0 { + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + err = baseClassDao.EditBaseClassById(tx, classId, baseClassData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + } + + responses.Ok(c) +} + +// AddBaseClass 新增基础分类 +func (b *BaseClass) AddBaseClass(c *gin.Context) { + baseClassRequest := requests.BaseClassRequest{} + req := baseClassRequest.AddBaseClass + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + baseClass := &model.BaseClass{ + ClassName: req.ClassName, + ClassStatus: 1, + ClassIcon: utils.RemoveOssDomain(req.ClassIcon), + ClassBrief: req.ClassBrief, + Sort: req.Sort, + } + + baseClassDao := dao.BaseClassDao{} + baseClass, err := baseClassDao.AddBaseClass(tx, baseClass) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/Coupon.go b/api/controller/Coupon.go new file mode 100644 index 0000000..f878f7e --- /dev/null +++ b/api/controller/Coupon.go @@ -0,0 +1,348 @@ +package controller + +import ( + "fmt" + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" + "time" +) + +type Coupon struct{} + +// GetCouponPage 获取系统优惠卷列表-分页 +func (b *Coupon) GetCouponPage(c *gin.Context) { + couponRequest := requests.CouponRequest{} + req := couponRequest.GetCouponPage + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + // 获取数据 + couponDao := dao.CouponDao{} + coupon, total, err := couponDao.GetCouponPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetCouponListDto(coupon) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetCoupon 获取系统优惠卷详情 +func (b *Coupon) GetCoupon(c *gin.Context) { + id := c.Param("coupon_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + couponId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + couponDao := dao.CouponDao{} + coupon, err := couponDao.GetCouponById(couponId) + if err != nil { + responses.FailWithMessage("优惠卷异常", c) + return + } + + // 处理返回值 + g := dto.GetCouponDto(coupon) + + responses.OkWithData(g, c) +} + +// PutCouponStatus 操作系统优惠卷状态 +func (b *Coupon) PutCouponStatus(c *gin.Context) { + couponRequest := requests.CouponRequest{} + req := couponRequest.PutCouponStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("coupon_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + couponId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取优惠卷数据 + couponDao := dao.CouponDao{} + coupon, err := couponDao.GetCouponById(couponId) + if err != nil { + responses.FailWithMessage("优惠卷异常", c) + return + } + + // 检测优惠卷删除状态 + if coupon.CouponStatus == req.CouponStatus { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + couponData := make(map[string]interface{}) + couponData["coupon_status"] = req.CouponStatus + err = couponDao.EditCouponById(tx, couponId, couponData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// AddSystemCoupon 新增系统优惠卷 +func (r *Coupon) AddSystemCoupon(c *gin.Context) { + couponRequest := requests.CouponRequest{} + req := couponRequest.AddSystemCoupon + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 满减 + if req.CouponType == 2 { + if *req.WithAmount == 0 { + responses.FailWithMessage("请填入符合满减标准金额", c) + return + } + } + + // 适用范围(1:全场通用 2:单项 3:会员) + if req.ApplicationScope == 1 { + // 全场通用 + if req.QuestionId == nil && req.SystemMemberId == nil { + responses.FailWithMessage("请填入关联选项", c) + return + } + } + + if req.ApplicationScope == 2 { + // 单项 + if req.QuestionId == nil { + responses.FailWithMessage("请填入关联算一算", c) + return + } + } + + // 适用范围(1:全场通用 2:单项 3:会员) + if req.ApplicationScope == 3 { + // 会员 + if req.SystemMemberId == nil { + responses.FailWithMessage("请填入关联会员", c) + return + } + } + + // 有效类型-绝对时效 + if req.ValidType == 1 { + if req.ValidStartTime == nil { + responses.FailWithMessage("请填入开始使用时间", c) + return + } + + if req.ValidEndTime == nil { + responses.FailWithMessage("请填入结束使用时间", c) + return + } + } + + // 有效类型-相对时效 + if req.ValidType == 2 { + if req.ValidDays == nil { + responses.FailWithMessage("请填入有效天数", c) + return + } + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + fmt.Println(r) + } + }() + + // 新增优惠卷表 + coupon := &model.Coupon{ + CouponName: req.CouponName, + CouponType: req.CouponType, + CouponStatus: 1, + ApplicationScope: req.ApplicationScope, + IsMutex: req.IsMutex, + CouponCount: req.CouponCount, + CouponTakeCount: 0, + CouponUsedCount: 0, + CouponPrice: req.CouponPrice, + ValidType: req.ValidType, + ValidStartTime: nil, + ValidEndTime: nil, + CouponDesc: req.CouponDesc, + QuestionId: nil, + SystemMemberId: nil, + } + + // 符合满减标准金额 + if req.WithAmount != nil { + coupon.WithAmount = req.WithAmount + } + + // 有效天数 + if req.ValidDays != nil { + coupon.ValidDays = req.ValidDays + } + + // 时间区间 + if req.ValidStartTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + t, err := time.ParseInLocation("2006-01-02 15:04:05", *req.ValidStartTime, location) + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + validStartTime := model.LocalTime(t) + + coupon.ValidStartTime = &validStartTime + } + + if req.ValidEndTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + t, err := time.ParseInLocation("2006-01-02 15:04:05", *req.ValidEndTime, location) + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + validEndTime := model.LocalTime(t) + coupon.ValidEndTime = &validEndTime + + // 检测结束时间-不允许当天结束 + now := time.Now() + year, month, day := now.Date() + endOfDay := time.Date(year, month, day, 23, 59, 59, 0, location) + if time.Time(validEndTime).Before(endOfDay) { + tx.Rollback() + responses.FailWithMessage("优惠卷过期时间最低设置为明天", c) + return + } + } + + // 关联单项id + if req.QuestionId != nil { + questionId, err := strconv.ParseInt(*req.QuestionId, 10, 64) + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + coupon.QuestionId = &questionId + } + + // 关联会员id + if req.SystemMemberId != nil { + systemMemberId, err := strconv.ParseInt(*req.SystemMemberId, 10, 64) + if err != nil { + tx.Rollback() + responses.FailWithMessage("新增失败", c) + return + } + + coupon.QuestionId = &systemMemberId + } + + couponDao := dao.CouponDao{} + coupon, err := couponDao.AddCoupon(tx, coupon) + if err != nil || coupon == nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + tx.Commit() + + responses.Ok(c) +} diff --git a/api/controller/OrderMember.go b/api/controller/OrderMember.go new file mode 100644 index 0000000..e8ff405 --- /dev/null +++ b/api/controller/OrderMember.go @@ -0,0 +1,219 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/api/service" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type OrderMember struct{} + +// GetOrderMemberPage 获取会员订单列表-分页 +func (b *OrderMember) GetOrderMemberPage(c *gin.Context) { + orderMemberRequest := requests.OrderMemberRequest{} + req := orderMemberRequest.GetOrderMemberPage + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + userId := c.GetInt64("UserId") + req.UserId = userId + + // 获取数据 + orderMemberDao := dao.OrderMemberDao{} + orderMember, total, err := orderMemberDao.GetOrderMemberPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetOrderMemberListDto(orderMember) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetOrderMember 获取会员订单详情 +func (b *OrderMember) GetOrderMember(c *gin.Context) { + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderMemberDao := dao.OrderMemberDao{} + orderMember, err := orderMemberDao.GetOrderMemberPreloadById(orderId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 处理返回值 + g := dto.GetOrderMemberDto(orderMember) + + // 加载会员数据 + g.LoadSystemMember(orderMember.SystemMember) + + // 加载用户数据 + g.LoadUserAttr(orderMember.User) + + responses.OkWithData(g, c) +} + +// PutCancelOrderMember 取消会员订单 +func (b *OrderMember) PutCancelOrderMember(c *gin.Context) { + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderMemberDao := dao.OrderMemberDao{} + orderMember, err := orderMemberDao.GetOrderMemberById(orderId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 取消单项订单 + orderMemberService := service.OrderMemberService{} + res, err := orderMemberService.CancelOrderMember(tx, orderMember.UserId, orderId, 2) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + if res == false { + tx.Rollback() + responses.FailWithMessage("取消订单失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// PutOrderMemberDeleteStatus 操作会员订单删除状态 +func (b *OrderMember) PutOrderMemberDeleteStatus(c *gin.Context) { + orderMemberRequest := requests.OrderMemberRequest{} + req := orderMemberRequest.PutOrderMemberDeleteStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.IsDelete != 0 && req.IsDelete != 1 { + responses.FailWithMessage("参数错误", c) + return + } + + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderMemberDao := dao.OrderMemberDao{} + orderMember, err := orderMemberDao.GetOrderMemberById(orderId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 检测订单状态 + if orderMember.OrderStatus == 2 { + responses.FailWithMessage("订单不允许删除", c) + return + } + + // 检测订单删除状态 + if orderMember.IsDelete == req.IsDelete { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + orderMemberData := make(map[string]interface{}) + orderMemberData["is_delete"] = req.IsDelete + err = orderMemberDao.EditOrderMemberById(tx, orderId, orderMemberData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("删除失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/OrderSingle.go b/api/controller/OrderSingle.go new file mode 100644 index 0000000..737bc29 --- /dev/null +++ b/api/controller/OrderSingle.go @@ -0,0 +1,274 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/api/service" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" + "time" +) + +type OrderSingle struct{} + +// GetOrderSinglePage 获取单项订单列表-分页 +func (b *OrderSingle) GetOrderSinglePage(c *gin.Context) { + orderSingleRequest := requests.OrderSingleRequest{} + req := orderSingleRequest.GetOrderSinglePage + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + userId := c.GetInt64("UserId") + req.UserId = userId + + // 获取数据 + orderSingleDao := dao.OrderSingleDao{} + orderSingles, total, err := orderSingleDao.GetOrderSinglePageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 获取单项配置 + systemSingleDao := dao.SystemSingleDao{} + + maps := make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + responses.FailWithMessage("内部错误", c) + return + } + + // 处理返回值 + g := dto.GetOrderSingleListDto(orderSingles) + + for _, singleDto := range g { + // 有效时间 + var validTime *time.Time + + // 计算有效期 + if singleDto.IsMember == 1 { + validTime = singleDto.MemberExpireDate + } else { + if singleDto.PayTime != nil { + payTime := singleDto.PayTime.Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour) + validTime = &payTime + } + } + + singleDto.LoadValidDate(validTime) + } + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// PutCancelOrderSingle 取消单项订单 +func (b *OrderSingle) PutCancelOrderSingle(c *gin.Context) { + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["order_id"] = orderId + orderSingle, err := orderSingleDao.GetOrderSingle(maps) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 取消单项订单 + orderSingleService := service.OrderSingleService{} + res, err := orderSingleService.CancelOrderSingle(tx, orderSingle.UserId, orderId, 2) + if err != nil { + tx.Rollback() + responses.FailWithMessage(err.Error(), c) + return + } + + if res == false { + tx.Rollback() + responses.FailWithMessage("取消订单失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// PutOrderSingleDeleteStatus 操作单项订单删除状态 +func (b *OrderSingle) PutOrderSingleDeleteStatus(c *gin.Context) { + orderSingleRequest := requests.OrderSingleRequest{} + req := orderSingleRequest.PutOrderSingleDeleteStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.IsDelete != 0 && req.IsDelete != 1 { + responses.FailWithMessage("参数错误", c) + return + } + + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + orderSingle, err := orderSingleDao.GetOrderSingleById(orderId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 检测订单状态 + if orderSingle.OrderStatus == 2 { + responses.FailWithMessage("订单不允许删除", c) + return + } + + // 检测订单删除状态 + if orderSingle.IsDelete == req.IsDelete { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + orderSingleData := make(map[string]interface{}) + orderSingleData["is_delete"] = 1 + err = orderSingleDao.EditOrderSingleById(tx, orderId, orderSingleData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("删除失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// GetOrderSingle 获取单项订单详情 +func (b *OrderSingle) GetOrderSingle(c *gin.Context) { + id := c.Param("order_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + orderId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + orderSingle, err := orderSingleDao.GetOrderSinglePreloadById(orderId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 获取单项配置 + systemSingleDao := dao.SystemSingleDao{} + + maps := make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + responses.FailWithMessage("内部错误", c) + return + } + + // 有效时间 + var validTime *time.Time + + // 计算有效期 + if orderSingle.User.IsMember == 1 { + validTime = orderSingle.User.MemberExpireDate + } else { + if orderSingle.PayTime != nil { + payTime := orderSingle.PayTime.Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour) + validTime = &payTime + } + } + + g := dto.GetOrderSingleDto(orderSingle) + + // 加载题目数据 + g.LoadQuestion(orderSingle.Question) + + // 加载到期时间 + g.LoadValidDate(validTime) + + // 加载用户数据 + g.LoadUserAttr(orderSingle.User) + + responses.OkWithData(g, c) +} diff --git a/api/controller/Public.go b/api/controller/Public.go new file mode 100644 index 0000000..f1b2d2b --- /dev/null +++ b/api/controller/Public.go @@ -0,0 +1,328 @@ +package controller + +import ( + "crypto/md5" + "encoding/hex" + "fmt" + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "time" +) + +type Public struct{} + +// Login 登陆 +func (b *Public) Login(c *gin.Context) { + publicRequest := requests.PublicRequest{} + req := publicRequest.Login + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 验证验证码 + if config.C.Env == "prod" { + isValid := utils.VerifyCaptcha(req.CaptchaId, req.Captcha) + if !isValid { + // 验证码错误 + responses.FailWithMessage("验证码错误", c) + return + } + } + + // 获取用户信息 + AdminUserDao := dao.AdminUserDao{} + maps := make(map[string]interface{}) + maps["access"] = req.Access + adminUser, err := AdminUserDao.GetAdminUser(maps) + if err != nil || adminUser == nil { + responses.FailWithMessage("用户名或密码错误", c) + return + } + + // 检测用户密码 + password := md5.Sum([]byte(req.Password + adminUser.Salt)) + // 将哈希值转换为16进制字符串 + passwordString := hex.EncodeToString(password[:]) + + fmt.Println(passwordString) + if passwordString != adminUser.Password { + responses.FailWithMessage("用户名或密码错误", c) + return + } + + // 检测用户状态 + if adminUser.IsDeleted == 1 { + responses.FailWithMessage("非法用户", c) + return + } + + if adminUser.IsDisabled == 1 { + responses.FailWithMessage("您的账号已被禁用,请联系管理员处理", c) + return + } + + // 下发token + token := &utils.Token{ + UserId: fmt.Sprintf("%d", adminUser.UserId), + } + + // 生成jwt + jwt, err := token.NewJWT() + if err != nil || jwt == "" { + responses.FailWithMessage("登陆失败", c) + return + } + + g := dto.AdminLoginDto(adminUser) + + g.LoadToken(jwt) + + responses.OkWithData(g, c) +} + +// GetCaptcha 获取验证码 +func (b *Public) GetCaptcha(c *gin.Context) { + id, b64s, err := utils.GenerateCaptcha() + if err != nil { + responses.FailWithMessage("验证码获取失败", c) + } + + responses.OkWithData(gin.H{ + "id": id, + "b64s": b64s, + }, c) +} + +// GetIndex 首页 +func (b *Public) GetIndex(c *gin.Context) { + userDao := dao.UserDao{} + questionDao := dao.QuestionDao{} + orderMemberDao := dao.OrderMemberDao{} + orderSingleDao := dao.OrderSingleDao{} + + // 获取问题数量 + maps := make(map[string]interface{}) + maps["question_status"] = 1 + questionCount, err := questionDao.GetQuestionCount(maps) + if err != nil { + questionCount = 0 + } + + // 获取用户数量 + maps = make(map[string]interface{}) + userCount, err := userDao.GetUserCount(maps) + if err != nil { + questionCount = 0 + } + + // 获取有效会员数 + maps = make(map[string]interface{}) + maps["user_status"] = 1 + maps["is_member"] = 1 + validMemberCount, err := userDao.GetUserCount(maps) + if err != nil { + validMemberCount = 0 + } + + // 获取问题总提交次数 + maps = make(map[string]interface{}) + questionSubmitCount, err := questionDao.GetQuestionSum(maps, "submit_count") + if err != nil { + questionSubmitCount = 0 + } + + // 获取问题总支付次数 + maps = make(map[string]interface{}) + questionPayCount, err := questionDao.GetQuestionSum(maps, "pay_count") + if err != nil { + questionPayCount = 0 + } + + // 获取会员购买次数 + maps = make(map[string]interface{}) + maps["order_status"] = 2 + maps["pay_status"] = 2 + maps["refund_status"] = 0 + maps["cancel_status"] = 0 + memberBuyCount, err := orderMemberDao.GetOrderMemberCount(maps) + if err != nil { + memberBuyCount = 0 + } + + // 获取会员购买总金额 + maps = make(map[string]interface{}) + maps["order_status"] = 2 + maps["pay_status"] = 2 + maps["refund_status"] = 0 + maps["cancel_status"] = 0 + memberAmountTotal, err := orderMemberDao.GetOrderMemberSum(maps, "payment_amount_total") + if err != nil { + memberAmountTotal = 0 + } + + // 获取会员购买总金额 + maps = make(map[string]interface{}) + maps["order_status"] = 2 + maps["pay_status"] = 2 + maps["refund_status"] = 0 + maps["cancel_status"] = 0 + singleAmountTotal, err := orderSingleDao.GetOrderSingleSum(maps, "payment_amount_total") + if err != nil { + singleAmountTotal = 0 + } + + g := dto.IndexDto{ + QuestionCount: questionCount, + UserCount: userCount, + ValidMemberCount: validMemberCount, + QuestionSubmitCount: int64(questionSubmitCount), + QuestionPayCount: int64(questionPayCount), + MemberBuyCount: memberBuyCount, + MemberAmountTotal: memberAmountTotal, + SingleAmountTotal: singleAmountTotal, + AmountTotal: memberAmountTotal + singleAmountTotal, + } + + responses.OkWithData(g, c) +} + +// GetIndexData 首页动态统计数据 +func (b *Public) GetIndexData(c *gin.Context) { + publicRequest := requests.PublicRequest{} + req := publicRequest.GetIndexData + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 返回值 + var g []*dto.IndexDataDto + results := make(map[string]int64) + + // 分类(1:新增用户数 2:新增算算数 3:新增单项支付数 4:新增会员购买数) + if req.Type == 1 { + // 新增用户数 + endTime, _ := time.Parse("2006-01-02", req.EndTime) + req.EndTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second).Format("2006-01-02 15:04:05") + + userDao := dao.UserDao{} + maps := make(map[string]interface{}) + users, err := userDao.GetUserListByTime(maps, req.StartTime, req.EndTime, "created_at") + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + for _, user := range users { + date := time.Time(user.CreatedAt).Format("2006-01-02") + results[date]++ + } + + } + + // 新增算算数 + if req.Type == 2 { + endTime, _ := time.Parse("2006-01-02", req.EndTime) + req.EndTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second).Format("2006-01-02 15:04:05") + + questionDao := dao.QuestionDao{} + maps := make(map[string]interface{}) + maps["question_status"] = 1 + questions, err := questionDao.GetQuestionListByTime(maps, req.StartTime, req.EndTime, "created_at") + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + for _, question := range questions { + date := time.Time(question.CreatedAt).Format("2006-01-02") + results[date]++ + } + } + + // 新增单项支付数 + if req.Type == 3 { + endTime, _ := time.Parse("2006-01-02", req.EndTime) + req.EndTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second).Format("2006-01-02 15:04:05") + + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["order_status"] = 2 + maps["pay_status"] = 2 + maps["refund_status"] = 0 + maps["cancel_status"] = 0 + orderSingles, err := orderSingleDao.GetOrderSingleListByTime(maps, req.StartTime, req.EndTime, "pay_time") + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + for _, orderSingle := range orderSingles { + date := orderSingle.PayTime.Format("2006-01-02") + results[date]++ + } + } + + // 新增会员购买数 + if req.Type == 4 { + endTime, _ := time.Parse("2006-01-02", req.EndTime) + req.EndTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second).Format("2006-01-02 15:04:05") + + orderMemberDao := dao.OrderMemberDao{} + maps := make(map[string]interface{}) + maps["order_status"] = 2 + maps["pay_status"] = 2 + maps["refund_status"] = 0 + maps["cancel_status"] = 0 + orderMembers, err := orderMemberDao.GetOrderMemberListByTime(maps, req.StartTime, req.EndTime, "pay_time") + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + for _, orderMember := range orderMembers { + date := orderMember.PayTime.Format("2006-01-02") + results[date]++ + } + } + + for k, v := range results { + response := &dto.IndexDataDto{ + Date: k, + Count: v, + } + + g = append(g, response) + } + + responses.OkWithData(g, c) +} diff --git a/api/controller/Question.go b/api/controller/Question.go new file mode 100644 index 0000000..0a849fd --- /dev/null +++ b/api/controller/Question.go @@ -0,0 +1,565 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/api/service" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" + "time" +) + +type Question struct{} + +// GetQuestionPage 获取问题列表-分页 +func (b *Question) GetQuestionPage(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.GetQuestionPage + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + // 获取数据 + questionDao := dao.QuestionDao{} + questions, total, err := questionDao.GetQuestionPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetQuestionListDto(questions) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetQuestionList 获取问题列表 +func (b *Question) GetQuestionList(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.GetQuestionList + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 获取数据 + questionDao := dao.QuestionDao{} + questions, err := questionDao.GetQuestionListSearch(req) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetQuestionListDto(questions) + + responses.OkWithData(g, c) +} + +// GetQuestion 获取问题详情 +func (b *Question) GetQuestion(c *gin.Context) { + id := c.Param("question_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取问题数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionPreloadById(questionId) + if err != nil { + responses.FailWithMessage("分类异常", c) + return + } + + // 处理返回值 + g := dto.GetQuestionDto(question) + + // 获取关联分类数据 + questionService := service.QuestionService{} + g.BaseClass, err = questionService.GetQuestionBaseClass(question.QuestionId) + + responses.OkWithData(g, c) +} + +// PutQuestion 修改问题 +func (b *Question) PutQuestion(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.PutQuestion + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.DiscountPrice != nil { + if req.DiscountEndTime == nil { + responses.FailWithMessage("请填写优惠过期时间", c) + return + } + + if *req.Price < *req.DiscountPrice { + responses.FailWithMessage("优惠价格不可超过原价", c) + return + } + } + + id := c.Param("question_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取问题数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(questionId) + if err != nil { + responses.FailWithMessage("问题异常", c) + return + } + + // 修改值 + questionData := make(map[string]interface{}) + + // 标题 + if req.QuestionTitle != question.QuestionTitle { + questionData["question_title"] = req.QuestionTitle + } + + // 副标题 + if req.QuestionSubtitle != question.QuestionSubtitle { + questionData["question_subtitle"] = req.QuestionSubtitle + } + + // 唯一标识 + if req.QuestionIden != question.QuestionIden { + questionData["question_iden"] = req.QuestionIden + } + + // 问题状态 + if req.QuestionStatus != question.QuestionStatus { + questionData["question_status"] = req.QuestionStatus + } + + // 是否隐藏 + if *req.IsHide != question.IsHide { + questionData["is_hide"] = req.IsHide + } + + // 是否推荐 + if *req.IsRecommend != question.IsRecommend { + questionData["is_recommend"] = req.IsRecommend + } + + // 点击次数 + if *req.ClickCount != question.ClickCount { + questionData["click_count"] = req.ClickCount + } + + // 提交次数 + if *req.SubmitCount != question.SubmitCount { + questionData["submit_count"] = req.SubmitCount + } + + // 支付次数 + if *req.PayCount != question.PayCount { + questionData["pay_count"] = req.PayCount + } + + // 价格 + if *req.Price != question.Price { + questionData["price"] = req.Price + } + + // 优惠价格 + if req.DiscountPrice != nil { + if req.DiscountEndTime == nil { + responses.FailWithMessage("请填写优惠过期时间", c) + return + } + if question.DiscountPrice == nil { + questionData["discount_price"] = req.DiscountPrice + } else { + if *req.DiscountPrice != *question.DiscountPrice { + questionData["discount_price"] = req.DiscountPrice + } + } + } else { + if question.DiscountPrice != nil { + questionData["discount_price"] = nil + } + } + + // 优惠截止时间 + if req.DiscountEndTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + discountEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", *req.DiscountEndTime, location) + if err != nil { + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + if question.DiscountEndTime == nil { + questionData["discount_end_time"] = discountEndTime + } else { + if *question.DiscountEndTime != discountEndTime { + questionData["discount_end_time"] = discountEndTime + } + } + } else { + if question.DiscountEndTime != nil { + questionData["discount_end_time"] = nil + } + } + + // 问题介绍 + if req.QuestionBrief != question.QuestionBrief { + questionData["question_brief"] = req.QuestionBrief + } + + // 问题解释/科普 + if req.QuestionExplain != question.QuestionExplain { + questionData["question_explain"] = req.QuestionExplain + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if len(questionData) > 0 { + err = questionDao.EditQuestionById(tx, question.QuestionId, questionData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + } + + // 删除问题关联分类 + questionClassDao := dao.QuestionClassDao{} + err = questionClassDao.DeleteQuestionClassByQuestionId(tx, questionId) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + // 新增问题关联分类 + for _, s := range req.ClassId { + // 将 id 转换为 int64 类型 + classId, err := strconv.ParseInt(*s, 10, 64) + if err != nil { + tx.Rollback() + responses.Fail(c) + return + } + + questionClass := &model.QuestionClass{ + QuestionId: questionId, + ClassId: classId, + } + + questionClass, err = questionClassDao.AddQuestionClass(tx, questionClass) + if err != nil { + tx.Rollback() + responses.Fail(c) + return + } + } + + tx.Commit() + responses.Ok(c) +} + +// AddQuestion 新增问题 +func (b *Question) AddQuestion(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.AddQuestion + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.DiscountPrice != nil { + if *req.Price < *req.DiscountPrice { + responses.FailWithMessage("优惠价格不可超过原价", c) + return + } + + if req.DiscountEndTime == nil { + responses.FailWithMessage("请填写优惠过期时间", c) + return + } + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + question := &model.Question{ + QuestionTitle: req.QuestionTitle, + QuestionSubtitle: req.QuestionSubtitle, + QuestionIden: req.QuestionIden, + QuestionStatus: req.QuestionStatus, + IsHide: *req.IsHide, + IsRecommend: *req.IsRecommend, + ClickCount: *req.ClickCount, + SubmitCount: *req.SubmitCount, + PayCount: *req.PayCount, + Price: *req.Price, + DiscountPrice: req.DiscountPrice, + QuestionBrief: req.QuestionBrief, + QuestionExplain: req.QuestionExplain, + } + + // 处理优惠截止时间 + if req.DiscountEndTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + tx.Rollback() + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + discountEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", *req.DiscountEndTime, location) + if err != nil { + tx.Rollback() + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + question.DiscountEndTime = &discountEndTime + } + + questionDao := dao.QuestionDao{} + question, err := questionDao.AddQuestion(tx, question) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + // 新增问题关联分类 + questionClassDao := dao.QuestionClassDao{} + for _, s := range req.ClassId { + // 将 id 转换为 int64 类型 + classId, err := strconv.ParseInt(*s, 10, 64) + if err != nil { + tx.Rollback() + responses.Fail(c) + return + } + + questionClass := &model.QuestionClass{ + QuestionId: question.QuestionId, + ClassId: classId, + } + + questionClass, err = questionClassDao.AddQuestionClass(tx, questionClass) + if err != nil { + tx.Rollback() + responses.Fail(c) + return + } + } + + tx.Commit() + responses.Ok(c) +} + +// PutQuestionStatus 操作问题发布状态 +func (b *Question) PutQuestionStatus(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.PutQuestionStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("question_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取基础分类数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(questionId) + if err != nil { + responses.FailWithMessage("题目异常", c) + return + } + + // 检测状态 + if question.QuestionStatus == req.QuestionStatus { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + questionData := make(map[string]interface{}) + questionData["question_status"] = req.QuestionStatus + err = questionDao.EditQuestionById(tx, questionId, questionData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} + +// PutQuestionHideStatus 操作问题隐藏状态 +func (b *Question) PutQuestionHideStatus(c *gin.Context) { + questionRequest := requests.QuestionRequest{} + req := questionRequest.PutQuestionHideStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("question_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + questionId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取基础分类数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(questionId) + if err != nil { + responses.FailWithMessage("题目异常", c) + return + } + + // 检测状态 + if question.IsHide == req.IsHide { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + questionData := make(map[string]interface{}) + questionData["is_hide"] = req.IsHide + err = questionDao.EditQuestionById(tx, questionId, questionData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/SystemMember.go b/api/controller/SystemMember.go new file mode 100644 index 0000000..d3ffcc5 --- /dev/null +++ b/api/controller/SystemMember.go @@ -0,0 +1,259 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" + "time" +) + +type SystemMember struct{} + +// GetSystemMemberList 获取会员配置列表 +func (b *SystemMember) GetSystemMemberList(c *gin.Context) { + // 获取数据 + systemMemberDao := dao.SystemMemberDao{} + systemMembers, err := systemMemberDao.GetSystemMemberListSearch() + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetSystemMemberListDto(systemMembers) + + responses.OkWithData(g, c) +} + +// GetSystemMember 获取会员配置详情 +func (b *SystemMember) GetSystemMember(c *gin.Context) { + id := c.Param("system_member_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + systemMemberId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取问题数据 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(systemMemberId) + if err != nil { + responses.FailWithMessage("数据异常", c) + return + } + + // 处理返回值 + g := dto.GetSystemMemberDto(systemMember) + + responses.OkWithData(g, c) +} + +// PutSystemMember 修改会员配置 +func (b *SystemMember) PutSystemMember(c *gin.Context) { + systemMemberRequest := requests.SystemMemberRequest{} + req := systemMemberRequest.PutSystemMember + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.DiscountPrice != nil { + if req.Price < *req.DiscountPrice { + responses.FailWithMessage("优惠价格不可超过原价", c) + return + } + + if req.DiscountEndTime == nil { + responses.FailWithMessage("请填写优惠过期时间", c) + return + } + } + + id := c.Param("system_member_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + systemMemberId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取问题数据 + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.GetSystemMemberById(systemMemberId) + if err != nil { + responses.FailWithMessage("数据异常", c) + return + } + + // 修改值 + systemMemberData := make(map[string]interface{}) + + // 会员天数 + if req.MemberDays != systemMember.MemberDays { + systemMemberData["member_days"] = req.MemberDays + } + + // 价格 + if req.Price != systemMember.Price { + systemMemberData["price"] = req.Price + } + + // 优惠价格 + if req.DiscountPrice != nil { + if systemMember.DiscountPrice == nil { + systemMemberData["discount_price"] = req.DiscountPrice + } else { + if *req.DiscountPrice != *systemMember.DiscountPrice { + systemMemberData["discount_price"] = req.DiscountPrice + } + } + } else { + if systemMember.DiscountPrice != nil { + systemMemberData["discount_price"] = nil + } + } + + // 优惠截止时间 + if req.DiscountEndTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + discountEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", *req.DiscountEndTime, location) + if err != nil { + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + if systemMember.DiscountEndTime == nil { + systemMemberData["discount_end_time"] = discountEndTime + } else { + if *systemMember.DiscountEndTime != discountEndTime { + systemMemberData["discount_end_time"] = discountEndTime + } + } + } else { + if systemMember.DiscountEndTime != nil { + systemMemberData["discount_end_time"] = nil + } + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if len(systemMemberData) > 0 { + err = systemMemberDao.EditSystemMemberById(tx, systemMember.SystemMemberId, systemMemberData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + } + + tx.Commit() + responses.Ok(c) +} + +// AddSystemMember 新增会员配置 +func (b *SystemMember) AddSystemMember(c *gin.Context) { + systemMemberRequest := requests.SystemMemberRequest{} + req := systemMemberRequest.AddSystemMember + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.DiscountPrice != nil { + if req.Price < *req.DiscountPrice { + responses.FailWithMessage("优惠价格不可超过原价", c) + return + } + + if req.DiscountEndTime == nil { + responses.FailWithMessage("请填写优惠过期时间", c) + return + } + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + systemMember := &model.SystemMember{ + MemberDays: req.MemberDays, + Price: req.Price, + DiscountPrice: req.DiscountPrice, + } + + // 处理优惠截止时间 + if req.DiscountEndTime != nil { + // 获取本地时区 + location, err := time.LoadLocation("Local") + if err != nil { + tx.Rollback() + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + discountEndTime, err := time.ParseInLocation("2006-01-02 15:04:05", *req.DiscountEndTime, location) + if err != nil { + tx.Rollback() + responses.FailWithMessage("优惠截止时间错误", c) + return + } + + systemMember.DiscountEndTime = &discountEndTime + } + + systemMemberDao := dao.SystemMemberDao{} + systemMember, err := systemMemberDao.AddSystemMember(tx, systemMember) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/SystemSingle.go b/api/controller/SystemSingle.go new file mode 100644 index 0000000..3d3212b --- /dev/null +++ b/api/controller/SystemSingle.go @@ -0,0 +1,170 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type SystemSingle struct{} + +// GetSystemSingleList 获取单项配置列表 +func (b *SystemSingle) GetSystemSingleList(c *gin.Context) { + // 获取数据 + systemSingleDao := dao.SystemSingleDao{} + SystemSingles, err := systemSingleDao.GetSystemSingleListSearch() + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetSystemSingleListDto(SystemSingles) + + responses.OkWithData(g, c) +} + +// GetSystemSingle 获取单项配置详情 +func (b *SystemSingle) GetSystemSingle(c *gin.Context) { + id := c.Param("system_single_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + systemSingleId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取数据 + systemSingleDao := dao.SystemSingleDao{} + SystemSingle, err := systemSingleDao.GetSystemSingleById(systemSingleId) + if err != nil { + responses.FailWithMessage("数据异常", c) + return + } + + // 处理返回值 + g := dto.GetSystemSingleDto(SystemSingle) + + responses.OkWithData(g, c) +} + +// PutSystemSingle 修改会员配置 +func (b *SystemSingle) PutSystemSingle(c *gin.Context) { + SystemSingleRequest := requests.SystemSingleRequest{} + req := SystemSingleRequest.PutSystemSingle + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("system_single_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + systemSingleId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取问题数据 + SystemSingleDao := dao.SystemSingleDao{} + systemSingle, err := SystemSingleDao.GetSystemSingleById(systemSingleId) + if err != nil { + responses.FailWithMessage("数据异常", c) + return + } + + // 修改值 + systemSingleData := make(map[string]interface{}) + + // 首次购买价格 + if req.FirstTimePrice != systemSingle.FirstTimePrice { + systemSingleData["first_time_price"] = req.FirstTimePrice + } + + // 购买后有效天数 + if req.ValidDays != systemSingle.ValidDays { + systemSingleData["valid_days"] = req.ValidDays + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + if len(systemSingleData) > 0 { + err = SystemSingleDao.EditSystemSingleById(tx, systemSingle.SystemSingleId, systemSingleData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + } + + tx.Commit() + responses.Ok(c) +} + +// AddSystemSingle 新增会员配置 +func (b *SystemSingle) AddSystemSingle(c *gin.Context) { + SystemSingleRequest := requests.SystemSingleRequest{} + req := SystemSingleRequest.AddSystemSingle + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + systemSingle := &model.SystemSingle{ + FirstTimePrice: req.FirstTimePrice, + ValidDays: req.ValidDays, + } + + systemSingleDao := dao.SystemSingleDao{} + systemSingle, err := systemSingleDao.AddSystemSingle(tx, systemSingle) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/User.go b/api/controller/User.go new file mode 100644 index 0000000..5461e3b --- /dev/null +++ b/api/controller/User.go @@ -0,0 +1,178 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type User struct{} + +// GetUserPage 获取用户列表-分页 +func (b *User) GetUserPage(c *gin.Context) { + userRequest := requests.UserRequest{} + req := userRequest.GetUserPage + if err := c.ShouldBindJSON(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + if req.Order != nil { + if req.Order.UpdatedAt != "" { + if req.Order.UpdatedAt != "desc" && req.Order.UpdatedAt != "asc" { + responses.FailWithMessage("排序字段错误", c) + return + } + } + + if req.Order.SingleSubmitCount != "" { + if req.Order.SingleSubmitCount != "desc" && req.Order.SingleSubmitCount != "asc" { + responses.FailWithMessage("排序字段错误", c) + return + } + } + + if req.Order.SinglePayCount != "" { + if req.Order.SinglePayCount != "desc" && req.Order.SinglePayCount != "asc" { + responses.FailWithMessage("排序字段错误", c) + return + } + } + + if req.Order.MemberBuyCount != "" { + if req.Order.MemberBuyCount != "desc" && req.Order.MemberBuyCount != "asc" { + responses.FailWithMessage("排序字段错误", c) + return + } + } + } + + // 获取数据 + userDao := dao.UserDao{} + user, total, err := userDao.GetUserPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetUserListDto(user) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// GetUser 获取用户详情 +func (b *User) GetUser(c *gin.Context) { + id := c.Param("user_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + userId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 处理返回值 + g := dto.GetUserDto(user) + + responses.OkWithData(g, c) +} + +// PutUserStatus 操作用户状态 +func (b *User) PutUserStatus(c *gin.Context) { + userRequest := requests.UserRequest{} + req := userRequest.PutUserStatus + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + id := c.Param("user_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + userId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取订单数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil { + responses.FailWithMessage("订单异常", c) + return + } + + // 检测订单删除状态 + if user.UserStatus == req.UserStatus { + responses.Ok(c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + userData := make(map[string]interface{}) + userData["user_status"] = req.UserStatus + err = userDao.EditUserById(tx, userId, userData) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/controller/UserCoupon.go b/api/controller/UserCoupon.go new file mode 100644 index 0000000..4c79f25 --- /dev/null +++ b/api/controller/UserCoupon.go @@ -0,0 +1,109 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/api/responses" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "strconv" +) + +type UserCoupon struct{} + +// GetUserCouponPage 获取用户优惠卷列表-分页 +func (b *UserCoupon) GetUserCouponPage(c *gin.Context) { + userCouponRequest := requests.UserCouponRequest{} + req := userCouponRequest.GetUserCouponPage + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + + if req.Page == 0 { + req.Page = 1 + } + + if req.PageSize == 0 { + req.PageSize = 20 + } + + // 获取数据 + userCouponDao := dao.UserCouponDao{} + userCoupons, total, err := userCouponDao.GetUserCouponPageSearch(req, req.Page, req.PageSize) + if err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 处理返回值 + g := dto.GetUserCouponListDto(userCoupons) + + result := make(map[string]interface{}) + result["page"] = req.Page + result["page_size"] = req.PageSize + result["total"] = total + result["data"] = g + responses.OkWithData(result, c) +} + +// DeleteUserCoupon 删除用户优惠卷 +func (b *UserCoupon) DeleteUserCoupon(c *gin.Context) { + id := c.Param("user_coupon_id") + if id == "" { + responses.FailWithMessage("缺少参数", c) + return + } + + // 将 id 转换为 int64 类型 + userCouponId, err := strconv.ParseInt(id, 10, 64) + if err != nil { + responses.Fail(c) + return + } + + // 获取优惠卷数据 + userCouponDao := dao.UserCouponDao{} + userCoupon, err := userCouponDao.GetUserCouponById(userCouponId) + if err != nil { + responses.FailWithMessage("优惠卷异常", c) + return + } + + // 检测优惠卷状态(0:未使用 1:已使用 3:已过期) + if userCoupon.UserCouponStatus == 1 { + responses.FailWithMessage("优惠卷已使用,无需删除", c) + return + } + + if userCoupon.UserCouponStatus == 3 { + responses.FailWithMessage("优惠卷已过期,无需删除", c) + return + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + err = userCouponDao.DeleteUserCouponById(tx, userCouponId) + if err != nil { + tx.Rollback() + responses.FailWithMessage("操作失败", c) + return + } + + tx.Commit() + responses.Ok(c) +} diff --git a/api/dao/AdminUser.go b/api/dao/AdminUser.go new file mode 100644 index 0000000..02df665 --- /dev/null +++ b/api/dao/AdminUser.go @@ -0,0 +1,108 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type AdminUserDao struct { +} + +// GetAdminUserById 获取数据-id +func (r *AdminUserDao) GetAdminUserById(AdminUserId int64) (m *model.AdminUser, err error) { + err = global.Db.First(&m, AdminUserId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetAdminUserPreloadById 获取数据-加载全部关联-id +func (r *AdminUserDao) GetAdminUserPreloadById(AdminUserId int64) (m *model.AdminUser, err error) { + err = global.Db.Preload(clause.Associations).First(&m, AdminUserId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteAdminUser 删除 +func (r *AdminUserDao) DeleteAdminUser(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.AdminUser{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteAdminUserById 删除-id +func (r *AdminUserDao) DeleteAdminUserById(tx *gorm.DB, AdminUserId int64) error { + if err := tx.Delete(&model.AdminUser{}, AdminUserId).Error; err != nil { + return err + } + return nil +} + +// EditAdminUser 修改 +func (r *AdminUserDao) EditAdminUser(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.AdminUser{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditAdminUserById 修改-id +func (r *AdminUserDao) EditAdminUserById(tx *gorm.DB, AdminUserId int64, data interface{}) error { + err := tx.Model(&model.AdminUser{}).Where("AdminUser_id = ?", AdminUserId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetAdminUserList 获取列表 +func (r *AdminUserDao) GetAdminUserList(maps interface{}) (m []*model.AdminUser, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetAdminUserCount 获取数量 +func (r *AdminUserDao) GetAdminUserCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.AdminUser{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetAdminUserListRand 获取列表-随机 +func (r *AdminUserDao) GetAdminUserListRand(maps interface{}, limit int) (m []*model.AdminUser, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddAdminUser 新增 +func (r *AdminUserDao) AddAdminUser(tx *gorm.DB, model *model.AdminUser) (*model.AdminUser, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetAdminUser 获取 +func (r *AdminUserDao) GetAdminUser(maps interface{}) (m *model.AdminUser, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/BaseAgreement.go b/api/dao/BaseAgreement.go new file mode 100644 index 0000000..df58b01 --- /dev/null +++ b/api/dao/BaseAgreement.go @@ -0,0 +1,130 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type BaseAgreementDao struct { +} + +// GetBaseAgreementById 获取数据-id +func (r *BaseAgreementDao) GetBaseAgreementById(agreementId int64) (m *model.BaseAgreement, err error) { + err = global.Db.First(&m, agreementId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseAgreementPreloadById 获取数据-加载全部关联-id +func (r *BaseAgreementDao) GetBaseAgreementPreloadById(agreementId int64) (m *model.BaseAgreement, err error) { + err = global.Db.Preload(clause.Associations).First(&m, agreementId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteBaseAgreement 删除 +func (r *BaseAgreementDao) DeleteBaseAgreement(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.BaseAgreement{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteBaseAgreementById 删除-id +func (r *BaseAgreementDao) DeleteBaseAgreementById(tx *gorm.DB, agreementId int64) error { + if err := tx.Delete(&model.BaseAgreement{}, agreementId).Error; err != nil { + return err + } + return nil +} + +// EditBaseAgreement 修改 +func (r *BaseAgreementDao) EditBaseAgreement(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.BaseAgreement{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditBaseAgreementById 修改-id +func (r *BaseAgreementDao) EditBaseAgreementById(tx *gorm.DB, agreementId int64, data interface{}) error { + err := tx.Model(&model.BaseAgreement{}).Where("agreement_id = ?", agreementId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetBaseAgreementList 获取列表 +func (r *BaseAgreementDao) GetBaseAgreementList(maps interface{}) (m []*model.BaseAgreement, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseAgreementCount 获取数量 +func (r *BaseAgreementDao) GetBaseAgreementCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.BaseAgreement{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetBaseAgreementListRand 获取列表-随机 +func (r *BaseAgreementDao) GetBaseAgreementListRand(maps interface{}, limit int) (m []*model.BaseAgreement, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddBaseAgreement 新增 +func (r *BaseAgreementDao) AddBaseAgreement(tx *gorm.DB, model *model.BaseAgreement) (*model.BaseAgreement, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetBaseAgreement 获取 +func (r *BaseAgreementDao) GetBaseAgreement(maps interface{}) (m *model.BaseAgreement, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseAgreementPageSearch 获取列表-分页 +func (r *BaseAgreementDao) GetBaseAgreementPageSearch(page, pageSize int) (m []*model.BaseAgreement, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.BaseAgreement{}) + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/BaseClass.go b/api/dao/BaseClass.go new file mode 100644 index 0000000..b84cd66 --- /dev/null +++ b/api/dao/BaseClass.go @@ -0,0 +1,180 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type BaseClassDao struct { +} + +// GetBaseClassById 获取数据-id +func (r *BaseClassDao) GetBaseClassById(classId int64) (m *model.BaseClass, err error) { + err = global.Db.First(&m, classId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseClassPreloadById 获取数据-加载全部关联-id +func (r *BaseClassDao) GetBaseClassPreloadById(classId int64) (m *model.BaseClass, err error) { + err = global.Db.Preload(clause.Associations).First(&m, classId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteBaseClass 删除 +func (r *BaseClassDao) DeleteBaseClass(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.BaseClass{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteBaseClassById 删除-id +func (r *BaseClassDao) DeleteBaseClassById(tx *gorm.DB, classId int64) error { + if err := tx.Delete(&model.BaseClass{}, classId).Error; err != nil { + return err + } + return nil +} + +// EditBaseClass 修改 +func (r *BaseClassDao) EditBaseClass(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.BaseClass{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditBaseClassById 修改-id +func (r *BaseClassDao) EditBaseClassById(tx *gorm.DB, classId int64, data interface{}) error { + err := tx.Model(&model.BaseClass{}).Where("class_id = ?", classId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetBaseClassList 获取列表 +func (r *BaseClassDao) GetBaseClassList(maps interface{}) (m []*model.BaseClass, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseClassCount 获取数量 +func (r *BaseClassDao) GetBaseClassCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.BaseClass{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetBaseClassListRand 获取列表-随机 +func (r *BaseClassDao) GetBaseClassListRand(maps interface{}, limit int) (m []*model.BaseClass, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddBaseClass 新增 +func (r *BaseClassDao) AddBaseClass(tx *gorm.DB, model *model.BaseClass) (*model.BaseClass, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetBaseClass 获取 +func (r *BaseClassDao) GetBaseClass(maps interface{}) (m *model.BaseClass, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetBaseClassPageSearch 获取列表-分页 +func (r *BaseClassDao) GetBaseClassPageSearch(req requests.GetBaseClassPage, page, pageSize int) (m []*model.BaseClass, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.BaseClass{}) + + // 分类名称 + if req.ClassName != "" { + query = query.Where("class_name LIKE ?", "%"+req.ClassName+"%") + } + + // 分类状态 + if req.ClassStatus != nil { + query = query.Where("class_status = ?", req.ClassStatus) + } + + // 排序 + if req.Order != nil { + if req.Order.Sort != "" { + query = query.Order("sort " + req.Order.Sort) + } + } + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} + +// GetBaseClassListSearch 获取列表 +func (r *BaseClassDao) GetBaseClassListSearch(req requests.GetBaseClassList) (m []*model.BaseClass, err error) { + // 构建查询条件 + query := global.Db.Model(&model.BaseClass{}) + + // 分类名称 + if req.ClassName != "" { + query = query.Where("class_name LIKE ?", "%"+req.ClassName+"%") + } + + // 分类状态 + if req.ClassStatus != nil { + query = query.Where("class_status = ?", req.ClassStatus) + } + + // 排序 + if req.Order != nil { + if req.Order.Sort != "" { + query = query.Order("sort " + req.Order.Sort) + } + } + + // 排序 + query = query.Order("created_at desc") + + err = query.Scopes(model.Paginate(1, 10)).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/Coupon.go b/api/dao/Coupon.go new file mode 100644 index 0000000..e9826fd --- /dev/null +++ b/api/dao/Coupon.go @@ -0,0 +1,174 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type CouponDao struct { +} + +// GetCouponById 获取数据-id +func (r *CouponDao) GetCouponById(CouponId int64) (m *model.Coupon, err error) { + err = global.Db.First(&m, CouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetCouponPreloadById 获取数据-加载全部关联-id +func (r *CouponDao) GetCouponPreloadById(CouponId int64) (m *model.Coupon, err error) { + err = global.Db.Preload(clause.Associations).First(&m, CouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteCoupon 删除 +func (r *CouponDao) DeleteCoupon(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.Coupon{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteCouponById 删除-id +func (r *CouponDao) DeleteCouponById(tx *gorm.DB, CouponId int64) error { + if err := tx.Delete(&model.Coupon{}, CouponId).Error; err != nil { + return err + } + return nil +} + +// EditCoupon 修改 +func (r *CouponDao) EditCoupon(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.Coupon{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditCouponById 修改-id +func (r *CouponDao) EditCouponById(tx *gorm.DB, CouponId int64, data interface{}) error { + err := tx.Model(&model.Coupon{}).Where("coupon_id = ?", CouponId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetCouponList 获取列表 +func (r *CouponDao) GetCouponList(maps interface{}) (m []*model.Coupon, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetCouponCount 获取数量 +func (r *CouponDao) GetCouponCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.Coupon{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetCouponListRand 获取列表-随机 +func (r *CouponDao) GetCouponListRand(maps interface{}, limit int) (m []*model.Coupon, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddCoupon 新增 +func (r *CouponDao) AddCoupon(tx *gorm.DB, model *model.Coupon) (*model.Coupon, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetCoupon 获取 +func (r *CouponDao) GetCoupon(maps interface{}) (m *model.Coupon, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetCouponListByValidTime 获取列表-今天开始时间/过期时间 +func (r *CouponDao) GetCouponListByValidTime(maps interface{}, startTime, endTime string) (m []*model.Coupon, err error) { + err = global.Db.Where(maps).Where("valid_end_time BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetCouponPageSearch 获取列表-分页 +func (r *CouponDao) GetCouponPageSearch(req requests.GetCouponPage, page, pageSize int) (m []*model.Coupon, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.Coupon{}) + + // 优惠券名称 + if req.CouponName != "" { + query = query.Where("coupon_name LIKE ?", "%"+req.CouponName+"%") + } + + // 优惠券类型 + if req.CouponType != nil { + query = query.Where("coupon_type = ?", req.CouponType) + } + + // 状态 + if req.CouponStatus != nil { + query = query.Where("coupon_status = ?", req.CouponStatus) + } + + // 适用范围 + if req.ApplicationScope != nil { + query = query.Where("application_scope = ?", req.ApplicationScope) + } + + // 是否互斥 + if req.IsMutex != nil { + query = query.Where("is_mutex = ?", req.IsMutex) + } + + // 有效类型 + if req.ValidType != nil { + query = query.Where("valid_type = ?", req.ValidType) + } + + // 优惠券描述 + if req.CouponDesc != "" { + query = query.Where("coupon_desc = ?", req.CouponDesc) + } + + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/OrderMember.go b/api/dao/OrderMember.go new file mode 100644 index 0000000..b1c3372 --- /dev/null +++ b/api/dao/OrderMember.go @@ -0,0 +1,226 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type OrderMemberDao struct { +} + +// GetOrderMemberById 获取数据-id +func (r *OrderMemberDao) GetOrderMemberById(OrderId int64) (m *model.OrderMember, err error) { + err = global.Db.First(&m, OrderId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberPreloadById 获取数据-加载全部关联-id +func (r *OrderMemberDao) GetOrderMemberPreloadById(OrderId int64) (m *model.OrderMember, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderMember 删除 +func (r *OrderMemberDao) DeleteOrderMember(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderMember{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderMemberById 删除-id +func (r *OrderMemberDao) DeleteOrderMemberById(tx *gorm.DB, OrderMemberId int64) error { + if err := tx.Delete(&model.OrderMember{}, OrderMemberId).Error; err != nil { + return err + } + return nil +} + +// EditOrderMember 修改 +func (r *OrderMemberDao) EditOrderMember(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderMember{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderMemberById 修改-id +func (r *OrderMemberDao) EditOrderMemberById(tx *gorm.DB, OrderId int64, data interface{}) error { + err := tx.Model(&model.OrderMember{}).Where("order_id = ?", OrderId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderMemberList 获取列表 +func (r *OrderMemberDao) GetOrderMemberList(maps interface{}) (m []*model.OrderMember, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberCount 获取数量 +func (r *OrderMemberDao) GetOrderMemberCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderMember{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderMemberListRand 获取列表-随机 +func (r *OrderMemberDao) GetOrderMemberListRand(maps interface{}, limit int) (m []*model.OrderMember, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderMember 新增 +func (r *OrderMemberDao) AddOrderMember(tx *gorm.DB, model *model.OrderMember) (*model.OrderMember, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderMember 获取 +func (r *OrderMemberDao) GetOrderMember(maps interface{}) (m *model.OrderMember, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserFirstTimeBuyOrderMember 获取用户首次购买的订单 +func (r *OrderMemberDao) GetUserFirstTimeBuyOrderMember(userId int64) (m *model.OrderMember, err error) { + err = global.Db. + Where("user_id = ?", userId). + Where("order_status != ?", 2). + First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberSum 获取数量 +func (r *OrderMemberDao) GetOrderMemberSum(maps interface{}, field string) (total float64, err error) { + var result struct { + Sum float64 + } + + err = global.Db.Model(&model.OrderMember{}).Where(maps).Select("SUM(" + field + ") as sum").Scan(&result).Error + if err != nil { + return 0, err + } + + return result.Sum, nil +} + +// GetOrderMemberListByTime 获取列表-开始时间/过期时间 +func (r *OrderMemberDao) GetOrderMemberListByTime(maps interface{}, startTime, endTime, field string) (m []*model.OrderMember, err error) { + err = global.Db.Where(maps).Where(field+" BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberPageSearch 获取列表-分页 +func (r *OrderMemberDao) GetOrderMemberPageSearch(req requests.GetOrderMemberPage, page, pageSize int) (m []*model.OrderMember, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.OrderMember{}) + query = query.Preload("SystemMember") + query = query.Preload("User") + + // 会员id + if req.SystemMemberId != "" { + query = query.Where("system_member_id = ?", req.SystemMemberId) + } + + // 订单状态(1:待支付 2:已完成 3:已取消) + if req.OrderStatus != nil { + query = query.Where("order_status = ?", req.OrderStatus) + } + + // 支付渠道 + if req.PayChannel != nil { + query = query.Where("pay_channel = ?", req.PayChannel) + } + + // 支付状态 + if req.PayStatus != nil { + query = query.Where("pay_status = ?", req.PayStatus) + } + + // 订单退款状态 + if req.RefundStatus != nil { + query = query.Where("refund_status = ?", req.RefundStatus) + } + + // 系统订单编号 + if req.OrderNo != "" { + query = query.Where("order_no = ?", req.OrderNo) + } + + // 第三方支付流水号 + if req.EscrowTradeNo != "" { + query = query.Where("escrow_trade_no = ?", req.EscrowTradeNo) + } + + // 取消状态 + if req.CancelStatus != nil { + query = query.Where("cancel_status = ?", req.CancelStatus) + } + + // 用户名称 + if req.UserName != "" { + subQuery := global.Db.Model(&model.User{}). + Select("user_id"). + Where("user_name LIKE ?", "%"+req.UserName+"%") + + query = query.Where(gorm.Expr("user_id IN (?)", subQuery)) + } + + // 会员天数 + if req.MemberDays != nil { + subQuery := global.Db.Model(&model.SystemMember{}). + Select("system_member_id"). + Where("member_days = ?", req.MemberDays) + + query = query.Where(gorm.Expr("system_member_id IN (?)", subQuery)) + } + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/OrderMemberCoupon.go b/api/dao/OrderMemberCoupon.go new file mode 100644 index 0000000..ae13d7a --- /dev/null +++ b/api/dao/OrderMemberCoupon.go @@ -0,0 +1,117 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type OrderMemberCouponDao struct { +} + +// GetOrderMemberCouponById 获取数据-id +func (r *OrderMemberCouponDao) GetOrderMemberCouponById(OrderMemberCouponId int64) (m *model.OrderMemberCoupon, err error) { + err = global.Db.First(&m, OrderMemberCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberCouponPreloadById 获取数据-加载全部关联-id +func (r *OrderMemberCouponDao) GetOrderMemberCouponPreloadById(OrderMemberCouponId int64) (m *model.OrderMemberCoupon, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderMemberCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderMemberCoupon 删除 +func (r *OrderMemberCouponDao) DeleteOrderMemberCoupon(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderMemberCoupon{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderMemberCouponById 删除-id +func (r *OrderMemberCouponDao) DeleteOrderMemberCouponById(tx *gorm.DB, OrderMemberCouponId int64) error { + if err := tx.Delete(&model.OrderMemberCoupon{}, OrderMemberCouponId).Error; err != nil { + return err + } + return nil +} + +// EditOrderMemberCoupon 修改 +func (r *OrderMemberCouponDao) EditOrderMemberCoupon(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderMemberCoupon{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderMemberCouponById 修改-id +func (r *OrderMemberCouponDao) EditOrderMemberCouponById(tx *gorm.DB, OrderMemberCouponId int64, data interface{}) error { + err := tx.Model(&model.OrderMemberCoupon{}).Where("order_coupon_id = ?", OrderMemberCouponId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderMemberCouponList 获取列表 +func (r *OrderMemberCouponDao) GetOrderMemberCouponList(maps interface{}) (m []*model.OrderMemberCoupon, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberCouponCount 获取数量 +func (r *OrderMemberCouponDao) GetOrderMemberCouponCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderMemberCoupon{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderMemberCouponListRand 获取列表-随机 +func (r *OrderMemberCouponDao) GetOrderMemberCouponListRand(maps interface{}, limit int) (m []*model.OrderMemberCoupon, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderMemberCoupon 新增 +func (r *OrderMemberCouponDao) AddOrderMemberCoupon(tx *gorm.DB, model *model.OrderMemberCoupon) (*model.OrderMemberCoupon, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderMemberCoupon 获取 +func (r *OrderMemberCouponDao) GetOrderMemberCoupon(maps interface{}) (m *model.OrderMemberCoupon, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberCouponByOrderId 获取数据-订单id +func (r *OrderMemberCouponDao) GetOrderMemberCouponByOrderId(orderId int64) (m *model.OrderMemberCoupon, err error) { + err = global.Db.Where("order_id = ?", orderId).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/OrderMemberRefund.go b/api/dao/OrderMemberRefund.go new file mode 100644 index 0000000..27a54f1 --- /dev/null +++ b/api/dao/OrderMemberRefund.go @@ -0,0 +1,108 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type OrderMemberRefundDao struct { +} + +// GetOrderMemberRefundById 获取数据-id +func (r *OrderMemberRefundDao) GetOrderMemberRefundById(OrderMemberRefundId int64) (m *model.OrderMemberRefund, err error) { + err = global.Db.First(&m, OrderMemberRefundId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberRefundPreloadById 获取数据-加载全部关联-id +func (r *OrderMemberRefundDao) GetOrderMemberRefundPreloadById(OrderMemberRefundId int64) (m *model.OrderMemberRefund, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderMemberRefundId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderMemberRefund 删除 +func (r *OrderMemberRefundDao) DeleteOrderMemberRefund(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderMemberRefund{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderMemberRefundById 删除-id +func (r *OrderMemberRefundDao) DeleteOrderMemberRefundById(tx *gorm.DB, OrderMemberRefundId int64) error { + if err := tx.Delete(&model.OrderMemberRefund{}, OrderMemberRefundId).Error; err != nil { + return err + } + return nil +} + +// EditOrderMemberRefund 修改 +func (r *OrderMemberRefundDao) EditOrderMemberRefund(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderMemberRefund{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderMemberRefundById 修改-id +func (r *OrderMemberRefundDao) EditOrderMemberRefundById(tx *gorm.DB, OrderMemberRefundId int64, data interface{}) error { + err := tx.Model(&model.OrderMemberRefund{}).Where("order_refund_id = ?", OrderMemberRefundId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderMemberRefundList 获取列表 +func (r *OrderMemberRefundDao) GetOrderMemberRefundList(maps interface{}) (m []*model.OrderMemberRefund, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderMemberRefundCount 获取数量 +func (r *OrderMemberRefundDao) GetOrderMemberRefundCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderMemberRefund{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderMemberRefundListRand 获取列表-随机 +func (r *OrderMemberRefundDao) GetOrderMemberRefundListRand(maps interface{}, limit int) (m []*model.OrderMemberRefund, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderMemberRefund 新增 +func (r *OrderMemberRefundDao) AddOrderMemberRefund(tx *gorm.DB, model *model.OrderMemberRefund) (*model.OrderMemberRefund, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderMemberRefund 获取 +func (r *OrderMemberRefundDao) GetOrderMemberRefund(maps interface{}) (m *model.OrderMemberRefund, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/OrderSingle.go b/api/dao/OrderSingle.go new file mode 100644 index 0000000..8260bfb --- /dev/null +++ b/api/dao/OrderSingle.go @@ -0,0 +1,236 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type OrderSingleDao struct { +} + +// GetOrderSingleById 获取数据-id +func (r *OrderSingleDao) GetOrderSingleById(OrderId int64) (m *model.OrderSingle, err error) { + err = global.Db.First(&m, OrderId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSinglePreloadById 获取数据-加载全部关联-id +func (r *OrderSingleDao) GetOrderSinglePreloadById(OrderId int64) (m *model.OrderSingle, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderSingle 删除 +func (r *OrderSingleDao) DeleteOrderSingle(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderSingle{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderSingleById 删除-id +func (r *OrderSingleDao) DeleteOrderSingleById(tx *gorm.DB, orderId int64) error { + if err := tx.Delete(&model.OrderSingle{}, orderId).Error; err != nil { + return err + } + return nil +} + +// EditOrderSingle 修改 +func (r *OrderSingleDao) EditOrderSingle(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderSingle{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderSingleById 修改-id +func (r *OrderSingleDao) EditOrderSingleById(tx *gorm.DB, orderId int64, data interface{}) error { + err := tx.Model(&model.OrderSingle{}).Where("order_id = ?", orderId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderSingleList 获取列表 +func (r *OrderSingleDao) GetOrderSingleList(maps interface{}) (m []*model.OrderSingle, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleCount 获取数量 +func (r *OrderSingleDao) GetOrderSingleCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderSingle{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderSingleListRand 获取列表-随机 +func (r *OrderSingleDao) GetOrderSingleListRand(maps interface{}, limit int) (m []*model.OrderSingle, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderSingle 新增 +func (r *OrderSingleDao) AddOrderSingle(tx *gorm.DB, model *model.OrderSingle) (*model.OrderSingle, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderSingle 获取 +func (r *OrderSingleDao) GetOrderSingle(maps interface{}) (m *model.OrderSingle, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleOrderList 获取列表-排序 +func (r *OrderSingleDao) GetOrderSingleOrderList(maps interface{}, orderField string, limit int) (m []*model.OrderSingle, err error) { + err = global.Db.Where(maps).Preload(clause.Associations).Order(orderField).Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserFirstTimeBuyOrderSingle 获取用户首次购买的订单 +func (r *OrderSingleDao) GetUserFirstTimeBuyOrderSingle(userId, questionId int64) (m *model.OrderSingle, err error) { + err = global.Db. + Where("user_id = ?", userId). + Where("question_id = ?", questionId). + Where("order_status != ?", 3). + First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleSum 获取数量 +func (r *OrderSingleDao) GetOrderSingleSum(maps interface{}, field string) (total float64, err error) { + var result struct { + Sum float64 + } + + err = global.Db.Model(&model.OrderSingle{}).Where(maps).Select("SUM(" + field + ") as sum").Scan(&result).Error + if err != nil { + return 0, err + } + + return result.Sum, nil +} + +// GetOrderSingleListByTime 获取列表-开始时间/过期时间 +func (r *OrderSingleDao) GetOrderSingleListByTime(maps interface{}, startTime, endTime, field string) (m []*model.OrderSingle, err error) { + err = global.Db.Where(maps).Where(field+" BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSinglePageSearch 获取列表-分页 +func (r *OrderSingleDao) GetOrderSinglePageSearch(req requests.GetOrderSinglePage, page, pageSize int) (m []*model.OrderSingle, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.OrderSingle{}) + query = query.Preload("Question") + query = query.Preload("User") + + // 问题id + if req.QuestionId != "" { + query = query.Where("question_id = ?", req.QuestionId) + } + + // 订单状态(1:待支付 2:已完成 3:已取消) + if req.OrderStatus != nil { + query = query.Where("order_status = ?", req.OrderStatus) + } + + // 支付渠道 + if req.PayChannel != nil { + query = query.Where("pay_channel = ?", req.PayChannel) + } + + // 支付状态 + if req.PayStatus != nil { + query = query.Where("pay_status = ?", req.PayStatus) + } + + // 订单退款状态 + if req.RefundStatus != nil { + query = query.Where("refund_status = ?", req.RefundStatus) + } + + // 系统订单编号 + if req.OrderNo != "" { + query = query.Where("order_no = ?", req.OrderNo) + } + + // 第三方支付流水号 + if req.EscrowTradeNo != "" { + query = query.Where("escrow_trade_no = ?", req.EscrowTradeNo) + } + + // 取消状态 + if req.CancelStatus != nil { + query = query.Where("cancel_status = ?", req.CancelStatus) + } + + // 问题标题 + if req.QuestionTitle != "" { + subQuery := global.Db.Model(&model.Question{}). + Select("question_id"). + Where("question_title LIKE ?", "%"+req.QuestionTitle+"%") + + query = query.Where(gorm.Expr("question_id IN (?)", subQuery)) + } + + // 用户名称 + if req.UserName != "" { + subQuery := global.Db.Model(&model.User{}). + Select("user_id"). + Where("user_name LIKE ?", "%"+req.UserName+"%") + + query = query.Where(gorm.Expr("user_id IN (?)", subQuery)) + } + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/OrderSingleCoupon.go b/api/dao/OrderSingleCoupon.go new file mode 100644 index 0000000..457c262 --- /dev/null +++ b/api/dao/OrderSingleCoupon.go @@ -0,0 +1,117 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type OrderSingleCouponDao struct { +} + +// GetOrderSingleCouponById 获取数据-id +func (r *OrderSingleCouponDao) GetOrderSingleCouponById(OrderSingleCouponId int64) (m *model.OrderSingleCoupon, err error) { + err = global.Db.First(&m, OrderSingleCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleCouponPreloadById 获取数据-加载全部关联-id +func (r *OrderSingleCouponDao) GetOrderSingleCouponPreloadById(OrderSingleCouponId int64) (m *model.OrderSingleCoupon, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderSingleCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleCouponByOrderId 获取数据-订单id +func (r *OrderSingleCouponDao) GetOrderSingleCouponByOrderId(orderId int64) (m *model.OrderSingleCoupon, err error) { + err = global.Db.Where("order_id = ?", orderId).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderSingleCoupon 删除 +func (r *OrderSingleCouponDao) DeleteOrderSingleCoupon(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderSingleCoupon{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderSingleCouponById 删除-id +func (r *OrderSingleCouponDao) DeleteOrderSingleCouponById(tx *gorm.DB, OrderSingleCouponId int64) error { + if err := tx.Delete(&model.OrderSingleCoupon{}, OrderSingleCouponId).Error; err != nil { + return err + } + return nil +} + +// EditOrderSingleCoupon 修改 +func (r *OrderSingleCouponDao) EditOrderSingleCoupon(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderSingleCoupon{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderSingleCouponById 修改-id +func (r *OrderSingleCouponDao) EditOrderSingleCouponById(tx *gorm.DB, OrderSingleCouponId int64, data interface{}) error { + err := tx.Model(&model.OrderSingleCoupon{}).Where("order_coupon_id = ?", OrderSingleCouponId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderSingleCouponList 获取列表 +func (r *OrderSingleCouponDao) GetOrderSingleCouponList(maps interface{}) (m []*model.OrderSingleCoupon, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleCouponCount 获取数量 +func (r *OrderSingleCouponDao) GetOrderSingleCouponCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderSingleCoupon{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderSingleCouponListRand 获取列表-随机 +func (r *OrderSingleCouponDao) GetOrderSingleCouponListRand(maps interface{}, limit int) (m []*model.OrderSingleCoupon, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderSingleCoupon 新增 +func (r *OrderSingleCouponDao) AddOrderSingleCoupon(tx *gorm.DB, model *model.OrderSingleCoupon) (*model.OrderSingleCoupon, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderSingleCoupon 获取 +func (r *OrderSingleCouponDao) GetOrderSingleCoupon(maps interface{}) (m *model.OrderSingleCoupon, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/OrderSingleRefund.go b/api/dao/OrderSingleRefund.go new file mode 100644 index 0000000..bea62f2 --- /dev/null +++ b/api/dao/OrderSingleRefund.go @@ -0,0 +1,108 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type OrderSingleRefundDao struct { +} + +// GetOrderSingleRefundById 获取数据-id +func (r *OrderSingleRefundDao) GetOrderSingleRefundById(OrderSingleRefundId int64) (m *model.OrderSingleRefund, err error) { + err = global.Db.First(&m, OrderSingleRefundId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleRefundPreloadById 获取数据-加载全部关联-id +func (r *OrderSingleRefundDao) GetOrderSingleRefundPreloadById(OrderSingleRefundId int64) (m *model.OrderSingleRefund, err error) { + err = global.Db.Preload(clause.Associations).First(&m, OrderSingleRefundId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteOrderSingleRefund 删除 +func (r *OrderSingleRefundDao) DeleteOrderSingleRefund(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.OrderSingleRefund{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteOrderSingleRefundById 删除-id +func (r *OrderSingleRefundDao) DeleteOrderSingleRefundById(tx *gorm.DB, OrderSingleRefundId int64) error { + if err := tx.Delete(&model.OrderSingleRefund{}, OrderSingleRefundId).Error; err != nil { + return err + } + return nil +} + +// EditOrderSingleRefund 修改 +func (r *OrderSingleRefundDao) EditOrderSingleRefund(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.OrderSingleRefund{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditOrderSingleRefundById 修改-id +func (r *OrderSingleRefundDao) EditOrderSingleRefundById(tx *gorm.DB, OrderSingleRefundId int64, data interface{}) error { + err := tx.Model(&model.OrderSingleRefund{}).Where("order_refund_id = ?", OrderSingleRefundId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetOrderSingleRefundList 获取列表 +func (r *OrderSingleRefundDao) GetOrderSingleRefundList(maps interface{}) (m []*model.OrderSingleRefund, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetOrderSingleRefundCount 获取数量 +func (r *OrderSingleRefundDao) GetOrderSingleRefundCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.OrderSingleRefund{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetOrderSingleRefundListRand 获取列表-随机 +func (r *OrderSingleRefundDao) GetOrderSingleRefundListRand(maps interface{}, limit int) (m []*model.OrderSingleRefund, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddOrderSingleRefund 新增 +func (r *OrderSingleRefundDao) AddOrderSingleRefund(tx *gorm.DB, model *model.OrderSingleRefund) (*model.OrderSingleRefund, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetOrderSingleRefund 获取 +func (r *OrderSingleRefundDao) GetOrderSingleRefund(maps interface{}) (m *model.OrderSingleRefund, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/Question.go b/api/dao/Question.go new file mode 100644 index 0000000..722fc22 --- /dev/null +++ b/api/dao/Question.go @@ -0,0 +1,423 @@ +package dao + +import ( + "errors" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type QuestionDao struct { +} + +// GetQuestionById 获取数据-id +func (r *QuestionDao) GetQuestionById(QuestionId int64) (m *model.Question, err error) { + err = global.Db.First(&m, QuestionId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionPreloadById 获取数据-加载全部关联-id +func (r *QuestionDao) GetQuestionPreloadById(QuestionId int64) (m *model.Question, err error) { + err = global.Db.Preload(clause.Associations).First(&m, QuestionId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteQuestion 删除 +func (r *QuestionDao) DeleteQuestion(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.Question{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteQuestionById 删除-id +func (r *QuestionDao) DeleteQuestionById(tx *gorm.DB, QuestionId int64) error { + if err := tx.Delete(&model.Question{}, QuestionId).Error; err != nil { + return err + } + return nil +} + +// EditQuestion 修改 +func (r *QuestionDao) EditQuestion(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.Question{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditQuestionById 修改-id +func (r *QuestionDao) EditQuestionById(tx *gorm.DB, QuestionId int64, data interface{}) error { + err := tx.Model(&model.Question{}).Where("question_id = ?", QuestionId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetQuestionList 获取列表 +func (r *QuestionDao) GetQuestionList(maps interface{}) (m []*model.Question, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionCount 获取数量 +func (r *QuestionDao) GetQuestionCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.Question{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetQuestionListRand 获取列表-随机 +func (r *QuestionDao) GetQuestionListRand(maps interface{}, limit int) (m []*model.Question, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddQuestion 新增 +func (r *QuestionDao) AddQuestion(tx *gorm.DB, model *model.Question) (*model.Question, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetQuestion 获取 +func (r *QuestionDao) GetQuestion(maps interface{}) (m *model.Question, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionOrderLimitList 获取列表-排序、限制数量 +func (r *QuestionDao) GetQuestionOrderLimitList(maps interface{}, orderField string, limit int) (m []*model.Question, err error) { + err = global.Db.Where(maps).Order(orderField).Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// Inc 自增 +func (r *QuestionDao) Inc(tx *gorm.DB, questionId int64, field string, numeral int) error { + err := tx.Model(&model.Question{}).Where("question_id = ?", questionId).UpdateColumn(field, gorm.Expr(field+" + ?", numeral)).Error + if err != nil { + return err + } + return nil +} + +// Dec 自减 +func (r *QuestionDao) Dec(tx *gorm.DB, questionId int64, field string, numeral int) error { + err := tx.Model(&model.Question{}).Where("question_id = ?", questionId).UpdateColumn(field, gorm.Expr(field+" - ?", numeral)).Error + if err != nil { + return err + } + return nil +} + +// GetQuestionSum 获取数量 +func (r *QuestionDao) GetQuestionSum(maps interface{}, field string) (total float64, err error) { + var result struct { + Sum float64 + } + + err = global.Db.Model(&model.Question{}).Where(maps).Select("SUM(" + field + ") as sum").Scan(&result).Error + if err != nil { + return 0, err + } + + return result.Sum, nil +} + +// GetQuestionListByTime 获取列表-开始时间/过期时间 +func (r *QuestionDao) GetQuestionListByTime(maps interface{}, startTime, endTime, field string) (m []*model.Question, err error) { + err = global.Db.Where(maps).Where(field+" BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionPageSearch 获取题目列表-分页 +func (r *QuestionDao) GetQuestionPageSearch(req requests.GetQuestionPage, page, pageSize int) (m []*model.Question, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.Question{}) + + // 是否隐藏 + query = query.Where("is_hide = ?", 0) + + // 主键id + if req.QuestionId != "" { + query = query.Where("question_id = ?", req.QuestionId) + } + + // 标题 + if req.QuestionTitle != "" { + query = query.Where("question_title LIKE ?", "%"+req.QuestionTitle+"%") + } + + // 副标题 + if req.QuestionSubtitle != "" { + query = query.Where("question_subtitle LIKE ?", "%"+req.QuestionSubtitle+"%") + } + + // 唯一标识 + if req.QuestionIden != "" { + query = query.Where("question_iden = ?", req.QuestionIden) + } + + // 问题状态 + if req.QuestionStatus != nil { + query = query.Where("question_status = ?", req.QuestionStatus) + } + + // 是否推荐 + if req.IsRecommend != nil { + query = query.Where("is_recommend = ?", req.IsRecommend) + } + + // 问题介绍 + if req.QuestionBrief != "" { + query = query.Where("question_brief LIKE ?", "%"+req.QuestionBrief+"%") + } + + // 问题解释/科普 + if req.QuestionExplain != "" { + query = query.Where("question_explain LIKE ?", "%"+req.QuestionExplain+"%") + } + + // 分类标识 + if req.ClassId != "" { + baseClassQuery := global.Db.Model(&model.BaseClass{}). + Select("class_id"). + Where("class_id = ?", req.ClassId) + + questionClassQuery := global.Db.Model(&model.QuestionClass{}). + Select("question_id"). + Where("class_id IN (?)", baseClassQuery) + + query = query.Where("question_id IN (?)", questionClassQuery) + } + + // 排序 + if req.Order != nil { + // 点击次数(点击进入详情页的人次) + if req.Order.ClickCount != "" { + if req.Order.ClickCount != "desc" && req.Order.ClickCount != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("click_count " + req.Order.ClickCount) + } + + // 提交次数(提交个人信息进行了算算的人次) + if req.Order.SubmitCount != "" { + if req.Order.SubmitCount != "desc" && req.Order.SubmitCount != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("submit_count " + req.Order.SubmitCount) + } + + // 支付次数(查看报告的人次) + if req.Order.PayCount != "" { + if req.Order.PayCount != "desc" && req.Order.PayCount != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("pay_count " + req.Order.PayCount) + } + + // 价格(原价) + if req.Order.Price != "" { + if req.Order.Price != "desc" && req.Order.Price != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("price " + req.Order.Price) + } + + // 优惠价格 + if req.Order.DiscountPrice != "" { + if req.Order.DiscountPrice != "desc" && req.Order.DiscountPrice != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("discount_price " + req.Order.DiscountPrice) + } + + if req.Order.UpdatedAt != "" { + if req.Order.UpdatedAt != "desc" && req.Order.UpdatedAt != "asc" { + return nil, 0, errors.New("排序字段错误") + } + + query = query.Order("updated_at " + req.Order.UpdatedAt) + } + } + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} + +// GetQuestionListSearch 获取题目列表 +func (r *QuestionDao) GetQuestionListSearch(req requests.GetQuestionList) (m []*model.Question, err error) { + // 构建查询条件 + query := global.Db.Model(&model.Question{}) + + // 主键id + if req.QuestionId != "" { + query = query.Where("question_id = ?", req.QuestionId) + } + + // 是否隐藏 + if req.IsHide != nil { + query = query.Where("is_hide = ?", req.IsHide) + } + + // 标题 + if req.QuestionTitle != "" { + query = query.Where("question_title LIKE ?", "%"+req.QuestionTitle+"%") + } + + // 副标题 + if req.QuestionSubtitle != "" { + query = query.Where("question_subtitle LIKE ?", "%"+req.QuestionSubtitle+"%") + } + + // 唯一标识 + if req.QuestionIden != "" { + query = query.Where("question_iden = ?", req.QuestionIden) + } + + // 问题状态 + if req.QuestionStatus != nil { + query = query.Where("question_status = ?", req.QuestionStatus) + } + + // 是否推荐 + if req.IsRecommend != nil { + query = query.Where("is_recommend = ?", req.IsRecommend) + } + + // 问题介绍 + if req.QuestionBrief != "" { + query = query.Where("question_brief LIKE ?", "%"+req.QuestionBrief+"%") + } + + // 问题解释/科普 + if req.QuestionExplain != "" { + query = query.Where("question_explain LIKE ?", "%"+req.QuestionExplain+"%") + } + + // 分类标识 + if req.ClassId != "" { + baseClassQuery := global.Db.Model(&model.BaseClass{}). + Select("class_id"). + Where("class_id = ?", req.ClassId) + + questionClassQuery := global.Db.Model(&model.QuestionClass{}). + Select("question_id"). + Where("class_id IN (?)", baseClassQuery) + + query = query.Where("question_id IN (?)", questionClassQuery) + } + + // 排序 + if req.Order != nil { + // 点击次数(点击进入详情页的人次) + if req.Order.ClickCount != "" { + if req.Order.ClickCount != "desc" && req.Order.ClickCount != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("click_count " + req.Order.ClickCount) + } + + // 提交次数(提交个人信息进行了算算的人次) + if req.Order.SubmitCount != "" { + if req.Order.SubmitCount != "desc" && req.Order.SubmitCount != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("submit_count " + req.Order.SubmitCount) + } + + // 支付次数(查看报告的人次) + if req.Order.PayCount != "" { + if req.Order.PayCount != "desc" && req.Order.PayCount != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("pay_count " + req.Order.PayCount) + } + + // 价格(原价) + if req.Order.Price != "" { + if req.Order.Price != "desc" && req.Order.Price != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("price " + req.Order.Price) + } + + // 优惠价格 + if req.Order.DiscountPrice != "" { + if req.Order.DiscountPrice != "desc" && req.Order.DiscountPrice != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("discount_price " + req.Order.DiscountPrice) + } + + if req.Order.UpdatedAt != "" { + if req.Order.UpdatedAt != "desc" && req.Order.UpdatedAt != "asc" { + return nil, errors.New("排序字段错误") + } + + query = query.Order("updated_at " + req.Order.UpdatedAt) + } + } + + // 排序 + query = query.Order("created_at desc") + + err = query.Scopes(model.Paginate(1, 10)).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/QuestionClass.go b/api/dao/QuestionClass.go new file mode 100644 index 0000000..23c31fa --- /dev/null +++ b/api/dao/QuestionClass.go @@ -0,0 +1,125 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type QuestionClassDao struct { +} + +// GetQuestionClassById 获取数据-id +func (r *QuestionClassDao) GetQuestionClassById(QuestionClassId int64) (m *model.QuestionClass, err error) { + err = global.Db.First(&m, QuestionClassId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionClassPreloadById 获取数据-加载全部关联-id +func (r *QuestionClassDao) GetQuestionClassPreloadById(QuestionClassId int64) (m *model.QuestionClass, err error) { + err = global.Db.Preload(clause.Associations).First(&m, QuestionClassId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteQuestionClass 删除 +func (r *QuestionClassDao) DeleteQuestionClass(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.QuestionClass{}).Error + if err != nil { + return err + } + return nil +} + +// GetQuestionClassListByQuestionId 获取数据-问题id +func (r *QuestionClassDao) GetQuestionClassListByQuestionId(questionId int64) (m []*model.QuestionClass, err error) { + err = global.Db.Where("question_id = ?", questionId).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteQuestionClassById 删除-id +func (r *QuestionClassDao) DeleteQuestionClassById(tx *gorm.DB, QuestionClassId int64) error { + if err := tx.Delete(&model.QuestionClass{}, QuestionClassId).Error; err != nil { + return err + } + return nil +} + +// DeleteQuestionClassByQuestionId 删除-问题id +func (r *QuestionClassDao) DeleteQuestionClassByQuestionId(tx *gorm.DB, questionId int64) error { + if err := tx.Where("question_id = ?", questionId).Delete(&model.QuestionClass{}).Error; err != nil { + return err + } + return nil +} + +// EditQuestionClass 修改 +func (r *QuestionClassDao) EditQuestionClass(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.QuestionClass{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditQuestionClassById 修改-id +func (r *QuestionClassDao) EditQuestionClassById(tx *gorm.DB, QuestionClassId int64, data interface{}) error { + err := tx.Model(&model.QuestionClass{}).Where("question_class_id = ?", QuestionClassId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetQuestionClassList 获取列表 +func (r *QuestionClassDao) GetQuestionClassList(maps interface{}) (m []*model.QuestionClass, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetQuestionClassCount 获取数量 +func (r *QuestionClassDao) GetQuestionClassCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.QuestionClass{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetQuestionClassListRand 获取列表-随机 +func (r *QuestionClassDao) GetQuestionClassListRand(maps interface{}, limit int) (m []*model.QuestionClass, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddQuestionClass 新增 +func (r *QuestionClassDao) AddQuestionClass(tx *gorm.DB, model *model.QuestionClass) (*model.QuestionClass, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetQuestionClass 获取 +func (r *QuestionClassDao) GetQuestionClass(maps interface{}) (m *model.QuestionClass, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/SystemMember.go b/api/dao/SystemMember.go new file mode 100644 index 0000000..56029a5 --- /dev/null +++ b/api/dao/SystemMember.go @@ -0,0 +1,123 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type SystemMemberDao struct { +} + +// GetSystemMemberById 获取数据-id +func (r *SystemMemberDao) GetSystemMemberById(SystemMemberId int64) (m *model.SystemMember, err error) { + err = global.Db.First(&m, SystemMemberId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemMemberPreloadById 获取数据-加载全部关联-id +func (r *SystemMemberDao) GetSystemMemberPreloadById(SystemMemberId int64) (m *model.SystemMember, err error) { + err = global.Db.Preload(clause.Associations).First(&m, SystemMemberId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteSystemMember 删除 +func (r *SystemMemberDao) DeleteSystemMember(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.SystemMember{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteSystemMemberById 删除-id +func (r *SystemMemberDao) DeleteSystemMemberById(tx *gorm.DB, SystemMemberId int64) error { + if err := tx.Delete(&model.SystemMember{}, SystemMemberId).Error; err != nil { + return err + } + return nil +} + +// EditSystemMember 修改 +func (r *SystemMemberDao) EditSystemMember(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.SystemMember{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditSystemMemberById 修改-id +func (r *SystemMemberDao) EditSystemMemberById(tx *gorm.DB, SystemMemberId int64, data interface{}) error { + err := tx.Model(&model.SystemMember{}).Where("system_member_id = ?", SystemMemberId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetSystemMemberList 获取列表 +func (r *SystemMemberDao) GetSystemMemberList(maps interface{}) (m []*model.SystemMember, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemMemberCount 获取数量 +func (r *SystemMemberDao) GetSystemMemberCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.SystemMember{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetSystemMemberListRand 获取列表-随机 +func (r *SystemMemberDao) GetSystemMemberListRand(maps interface{}, limit int) (m []*model.SystemMember, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddSystemMember 新增 +func (r *SystemMemberDao) AddSystemMember(tx *gorm.DB, model *model.SystemMember) (*model.SystemMember, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetSystemMember 获取 +func (r *SystemMemberDao) GetSystemMember(maps interface{}) (m *model.SystemMember, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemMemberListSearch 获取列表 +func (r *SystemMemberDao) GetSystemMemberListSearch() (m []*model.SystemMember, err error) { + // 构建查询条件 + query := global.Db.Model(&model.SystemMember{}) + + // 排序 + query = query.Order("created_at desc") + + err = query.Scopes(model.Paginate(1, 10)).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/SystemSingle.go b/api/dao/SystemSingle.go new file mode 100644 index 0000000..7667775 --- /dev/null +++ b/api/dao/SystemSingle.go @@ -0,0 +1,123 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type SystemSingleDao struct { +} + +// GetSystemSingleById 获取数据-id +func (r *SystemSingleDao) GetSystemSingleById(SystemSingleId int64) (m *model.SystemSingle, err error) { + err = global.Db.First(&m, SystemSingleId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemSinglePreloadById 获取数据-加载全部关联-id +func (r *SystemSingleDao) GetSystemSinglePreloadById(SystemSingleId int64) (m *model.SystemSingle, err error) { + err = global.Db.Preload(clause.Associations).First(&m, SystemSingleId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteSystemSingle 删除 +func (r *SystemSingleDao) DeleteSystemSingle(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.SystemSingle{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteSystemSingleById 删除-id +func (r *SystemSingleDao) DeleteSystemSingleById(tx *gorm.DB, SystemSingleId int64) error { + if err := tx.Delete(&model.SystemSingle{}, SystemSingleId).Error; err != nil { + return err + } + return nil +} + +// EditSystemSingle 修改 +func (r *SystemSingleDao) EditSystemSingle(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.SystemSingle{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditSystemSingleById 修改-id +func (r *SystemSingleDao) EditSystemSingleById(tx *gorm.DB, SystemSingleId int64, data interface{}) error { + err := tx.Model(&model.SystemSingle{}).Where("system_single_id = ?", SystemSingleId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetSystemSingleList 获取列表 +func (r *SystemSingleDao) GetSystemSingleList(maps interface{}) (m []*model.SystemSingle, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemSingleCount 获取数量 +func (r *SystemSingleDao) GetSystemSingleCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.SystemSingle{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetSystemSingleListRand 获取列表-随机 +func (r *SystemSingleDao) GetSystemSingleListRand(maps interface{}, limit int) (m []*model.SystemSingle, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddSystemSingle 新增 +func (r *SystemSingleDao) AddSystemSingle(tx *gorm.DB, model *model.SystemSingle) (*model.SystemSingle, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetSystemSingle 获取 +func (r *SystemSingleDao) GetSystemSingle(maps interface{}) (m *model.SystemSingle, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetSystemSingleListSearch 获取列表 +func (r *SystemSingleDao) GetSystemSingleListSearch() (m []*model.SystemSingle, err error) { + // 构建查询条件 + query := global.Db.Model(&model.SystemSingle{}) + + // 排序 + query = query.Order("created_at desc") + + err = query.Scopes(model.Paginate(1, 10)).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/User.go b/api/dao/User.go new file mode 100644 index 0000000..cc582a3 --- /dev/null +++ b/api/dao/User.go @@ -0,0 +1,223 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" + "strings" + "time" +) + +type UserDao struct { +} + +// GetUserById 获取数据-id +func (r *UserDao) GetUserById(UserId int64) (m *model.User, err error) { + err = global.Db.First(&m, UserId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserPreloadById 获取数据-加载全部关联-id +func (r *UserDao) GetUserPreloadById(UserId int64) (m *model.User, err error) { + err = global.Db.Preload(clause.Associations).First(&m, UserId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteUser 删除 +func (r *UserDao) DeleteUser(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.User{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteUserById 删除-id +func (r *UserDao) DeleteUserById(tx *gorm.DB, UserId int64) error { + if err := tx.Delete(&model.User{}, UserId).Error; err != nil { + return err + } + return nil +} + +// EditUser 修改 +func (r *UserDao) EditUser(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.User{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditUserById 修改-id +func (r *UserDao) EditUserById(tx *gorm.DB, UserId int64, data interface{}) error { + err := tx.Model(&model.User{}).Where("user_id = ?", UserId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetUserList 获取列表 +func (r *UserDao) GetUserList(maps interface{}) (m []*model.User, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCount 获取数量 +func (r *UserDao) GetUserCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.User{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetUserListRand 获取列表-随机 +func (r *UserDao) GetUserListRand(maps interface{}, limit int) (m []*model.User, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddUser 新增 +func (r *UserDao) AddUser(tx *gorm.DB, model *model.User) (*model.User, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetUser 获取 +func (r *UserDao) GetUser(maps interface{}) (m *model.User, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserListByTime 获取列表-今天开始时间/过期时间 +func (r *UserDao) GetUserListByTime(maps interface{}, startTime, endTime, field string) (m []*model.User, err error) { + err = global.Db.Where(maps).Where(field+" BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserPageSearch 获取列表-分页 +func (r *UserDao) GetUserPageSearch(req requests.GetUserPage, page, pageSize int) (m []*model.User, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.User{}) + + // 用户id + if req.UserId != "" { + query = query.Where("user_id = ?", req.UserId) + } + + // 用户名称 + if req.UserName != "" { + query = query.Where("user_name LIKE ?", "%"+req.UserName+"%") + } + + // 手机号 + if req.Mobile != "" { + query = query.Where("mobile LIKE ?", "%"+req.Mobile+"%") + } + + // 状态 + if req.UserStatus != 0 { + query = query.Where("user_status = ?", req.UserStatus) + } + + // 注册来源 + if req.RegisterSource != 0 { + query = query.Where("register_source = ?", req.RegisterSource) + } + + // 用户微信标识 + if req.OpenId != "" { + query = query.Where("open_id LIKE ?", "%"+req.OpenId+"%") + } + + // 性别 + if req.Sex != nil { + query = query.Where("sex = ?", req.Sex) + } + + // 是否会员 + if req.IsMember != nil { + query = query.Where("is_member = ?", req.IsMember) + } + + // 会员到期时间 + if req.MemberExpireDate != "" { + memberExpireDate := strings.Split(req.MemberExpireDate, "&") + if len(memberExpireDate) == 2 { + startTime, _ := time.Parse("2006-01-02", memberExpireDate[0]) + endTime, _ := time.Parse("2006-01-02", memberExpireDate[1]) + + endTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + + query = query.Where("member_expire_date BETWEEN ? AND ?", startTime, endTime) + } + } + + // 信息完善状态 + if req.IsInfoComplete != nil { + subQuery := global.Db.Model(&model.UserInfo{}). + Where("user_info.user_id = user.user_id"). + Where("user_info.nation_id is not NULL"). + Where("user_info.disease_class_id is not NULL").Select("1") + + query = query.Where("EXISTS (?)", subQuery) + } + + // 排序 + if req.Order != nil { + if req.Order.UpdatedAt != "" { + query = query.Order("updated_at " + req.Order.UpdatedAt) + } + + if req.Order.SingleSubmitCount != "" { + query = query.Order("single_submit_count " + req.Order.SingleSubmitCount) + } + + if req.Order.SinglePayCount != "" { + query = query.Order("single_pay_count " + req.Order.SinglePayCount) + } + + if req.Order.MemberBuyCount != "" { + query = query.Order("member_buy_count " + req.Order.MemberBuyCount) + } + } + + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/UserCollection.go b/api/dao/UserCollection.go new file mode 100644 index 0000000..312f72a --- /dev/null +++ b/api/dao/UserCollection.go @@ -0,0 +1,108 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type UserCollectionDao struct { +} + +// GetUserCollectionById 获取数据-id +func (r *UserCollectionDao) GetUserCollectionById(UserCollectionId int64) (m *model.UserCollection, err error) { + err = global.Db.First(&m, UserCollectionId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCollectionPreloadById 获取数据-加载全部关联-id +func (r *UserCollectionDao) GetUserCollectionPreloadById(UserCollectionId int64) (m *model.UserCollection, err error) { + err = global.Db.Preload(clause.Associations).First(&m, UserCollectionId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteUserCollection 删除 +func (r *UserCollectionDao) DeleteUserCollection(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.UserCollection{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteUserCollectionById 删除-id +func (r *UserCollectionDao) DeleteUserCollectionById(tx *gorm.DB, UserCollectionId int64) error { + if err := tx.Delete(&model.UserCollection{}, UserCollectionId).Error; err != nil { + return err + } + return nil +} + +// EditUserCollection 修改 +func (r *UserCollectionDao) EditUserCollection(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.UserCollection{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditUserCollectionById 修改-id +func (r *UserCollectionDao) EditUserCollectionById(tx *gorm.DB, UserCollectionId int64, data interface{}) error { + err := tx.Model(&model.UserCollection{}).Where("user_coupon_id = ?", UserCollectionId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetUserCollectionList 获取列表 +func (r *UserCollectionDao) GetUserCollectionList(maps interface{}) (m []*model.UserCollection, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCollectionCount 获取数量 +func (r *UserCollectionDao) GetUserCollectionCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.UserCollection{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetUserCollectionListRand 获取列表-随机 +func (r *UserCollectionDao) GetUserCollectionListRand(maps interface{}, limit int) (m []*model.UserCollection, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddUserCollection 新增 +func (r *UserCollectionDao) AddUserCollection(tx *gorm.DB, model *model.UserCollection) (*model.UserCollection, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetUserCollection 获取 +func (r *UserCollectionDao) GetUserCollection(maps interface{}) (m *model.UserCollection, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dao/UserCoupon.go b/api/dao/UserCoupon.go new file mode 100644 index 0000000..ef669f6 --- /dev/null +++ b/api/dao/UserCoupon.go @@ -0,0 +1,181 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/api/requests" + "hepa-calc-admin-api/global" +) + +type UserCouponDao struct { +} + +// GetUserCouponById 获取数据-id +func (r *UserCouponDao) GetUserCouponById(UserCouponId int64) (m *model.UserCoupon, err error) { + err = global.Db.First(&m, UserCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCouponPreloadById 获取数据-加载全部关联-id +func (r *UserCouponDao) GetUserCouponPreloadById(UserCouponId int64) (m *model.UserCoupon, err error) { + err = global.Db.Preload(clause.Associations).First(&m, UserCouponId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteUserCoupon 删除 +func (r *UserCouponDao) DeleteUserCoupon(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.UserCoupon{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteUserCouponById 删除-id +func (r *UserCouponDao) DeleteUserCouponById(tx *gorm.DB, UserCouponId int64) error { + if err := tx.Delete(&model.UserCoupon{}, UserCouponId).Error; err != nil { + return err + } + return nil +} + +// EditUserCoupon 修改 +func (r *UserCouponDao) EditUserCoupon(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.UserCoupon{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditUserCouponById 修改-id +func (r *UserCouponDao) EditUserCouponById(tx *gorm.DB, UserCouponId int64, data interface{}) error { + err := tx.Model(&model.UserCoupon{}).Where("user_coupon_id = ?", UserCouponId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetUserCouponList 获取列表 +func (r *UserCouponDao) GetUserCouponList(maps interface{}) (m []*model.UserCoupon, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCouponPreloadList 获取列表-加载全部关联 +func (r *UserCouponDao) GetUserCouponPreloadList(maps interface{}) (m []*model.UserCoupon, err error) { + err = global.Db.Preload(clause.Associations).Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCouponCount 获取数量 +func (r *UserCouponDao) GetUserCouponCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.UserCoupon{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetUserCouponListRand 获取列表-随机 +func (r *UserCouponDao) GetUserCouponListRand(maps interface{}, limit int) (m []*model.UserCoupon, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddUserCoupon 新增 +func (r *UserCouponDao) AddUserCoupon(tx *gorm.DB, model *model.UserCoupon) (*model.UserCoupon, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetUserCoupon 获取 +func (r *UserCouponDao) GetUserCoupon(maps interface{}) (m *model.UserCoupon, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCouponListByValidTime 获取列表-今天开始时间/过期时间 +func (r *UserCouponDao) GetUserCouponListByValidTime(maps interface{}, startTime, endTime string) (m []*model.UserCoupon, err error) { + err = global.Db.Where(maps).Where("valid_end_time BETWEEN ? AND ?", startTime, endTime).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserCouponPageSearch 获取列表-分页 +func (r *UserCouponDao) GetUserCouponPageSearch(req requests.GetUserCouponPage, page, pageSize int) (m []*model.UserCoupon, total int64, err error) { + var totalRecords int64 + + // 构建查询条件 + query := global.Db.Model(&model.UserCoupon{}) + + // 优惠卷 + query = query.Preload("Coupon") + + // 优惠券id + if req.CouponId != "" { + query = query.Where("coupon_id = ?", req.CouponId) + } + + // 用户id + if req.UserId != nil { + query = query.Where("user_id = ?", req.UserId) + } + + // 优惠券id + if req.CouponId != "" { + query = query.Where("coupon_id = ?", req.CouponId) + } + + // 状态(0:未使用 1:已使用 3:已过期) + if req.UserCouponStatus != nil { + query = query.Where("user_coupon_status = ?", req.UserCouponStatus) + } + + // 用户名称 + if req.UserName != "" { + subQuery := global.Db.Model(&model.User{}). + Select("user_id"). + Where("user_name LIKE ?", "%"+req.UserName+"%") + + query = query.Where(gorm.Expr("user_id IN (?)", subQuery)) + } + + // 排序 + query = query.Order("created_at desc") + + // 查询总数量 + if err := query.Count(&totalRecords).Error; err != nil { + return nil, 0, err + } + + err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + if err != nil { + return nil, 0, err + } + return m, totalRecords, nil +} diff --git a/api/dao/UserInfo.go b/api/dao/UserInfo.go new file mode 100644 index 0000000..5a62e88 --- /dev/null +++ b/api/dao/UserInfo.go @@ -0,0 +1,108 @@ +package dao + +import ( + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type UserInfoDao struct { +} + +// GetUserInfoById 获取数据-id +func (r *UserInfoDao) GetUserInfoById(UserInfoId int64) (m *model.UserInfo, err error) { + err = global.Db.First(&m, UserInfoId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserInfoPreloadById 获取数据-加载全部关联-id +func (r *UserInfoDao) GetUserInfoPreloadById(UserInfoId int64) (m *model.UserInfo, err error) { + err = global.Db.Preload(clause.Associations).First(&m, UserInfoId).Error + if err != nil { + return nil, err + } + return m, nil +} + +// DeleteUserInfo 删除 +func (r *UserInfoDao) DeleteUserInfo(tx *gorm.DB, maps interface{}) error { + err := tx.Where(maps).Delete(&model.UserInfo{}).Error + if err != nil { + return err + } + return nil +} + +// DeleteUserInfoById 删除-id +func (r *UserInfoDao) DeleteUserInfoById(tx *gorm.DB, UserInfoId int64) error { + if err := tx.Delete(&model.UserInfo{}, UserInfoId).Error; err != nil { + return err + } + return nil +} + +// EditUserInfo 修改 +func (r *UserInfoDao) EditUserInfo(tx *gorm.DB, maps interface{}, data interface{}) error { + err := tx.Model(&model.UserInfo{}).Where(maps).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// EditUserInfoById 修改-id +func (r *UserInfoDao) EditUserInfoById(tx *gorm.DB, UserInfoId int64, data interface{}) error { + err := tx.Model(&model.UserInfo{}).Where("user_info_id = ?", UserInfoId).Updates(data).Error + if err != nil { + return err + } + return nil +} + +// GetUserInfoList 获取列表 +func (r *UserInfoDao) GetUserInfoList(maps interface{}) (m []*model.UserInfo, err error) { + err = global.Db.Where(maps).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// GetUserInfoCount 获取数量 +func (r *UserInfoDao) GetUserInfoCount(maps interface{}) (total int64, err error) { + err = global.Db.Model(&model.UserInfo{}).Where(maps).Count(&total).Error + if err != nil { + return 0, err + } + return total, nil +} + +// GetUserInfoListRand 获取列表-随机 +func (r *UserInfoDao) GetUserInfoListRand(maps interface{}, limit int) (m []*model.UserInfo, err error) { + err = global.Db.Where(maps).Order("rand()").Limit(limit).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + +// AddUserInfo 新增 +func (r *UserInfoDao) AddUserInfo(tx *gorm.DB, model *model.UserInfo) (*model.UserInfo, error) { + if err := tx.Create(model).Error; err != nil { + return nil, err + } + return model, nil +} + +// GetUserInfo 获取 +func (r *UserInfoDao) GetUserInfo(maps interface{}) (m *model.UserInfo, err error) { + err = global.Db.Where(maps).First(&m).Error + if err != nil { + return nil, err + } + return m, nil +} diff --git a/api/dto/BaseAgreement.go b/api/dto/BaseAgreement.go new file mode 100644 index 0000000..81b7678 --- /dev/null +++ b/api/dto/BaseAgreement.go @@ -0,0 +1,49 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +// BaseAgreementDto 基础数据-协议 +type BaseAgreementDto struct { + AgreementId string `json:"agreement_id"` // 主键id + AgreementTitle string `json:"agreement_title"` // 协议标题 + AgreementContent string `json:"agreement_content"` // 协议内容 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 +} + +// GetBaseAgreementDto 详情-协议-分类表 +func GetBaseAgreementDto(m *model.BaseAgreement) *BaseAgreementDto { + return &BaseAgreementDto{ + AgreementId: fmt.Sprintf("%d", m.AgreementId), + AgreementTitle: m.AgreementTitle, + AgreementContent: m.AgreementContent, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// GetBaseAgreementListDto 列表-基础数据-协议 +func GetBaseAgreementListDto(m []*model.BaseAgreement) []*BaseAgreementDto { + // 处理返回值 + responses := make([]*BaseAgreementDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &BaseAgreementDto{ + AgreementId: fmt.Sprintf("%d", v.AgreementId), + AgreementTitle: v.AgreementTitle, + AgreementContent: v.AgreementContent, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} diff --git a/api/dto/BaseClass.go b/api/dto/BaseClass.go new file mode 100644 index 0000000..c8f13e5 --- /dev/null +++ b/api/dto/BaseClass.go @@ -0,0 +1,59 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" +) + +// BaseClassDto 基础数据-分类表 +type BaseClassDto struct { + ClassId string `json:"class_id"` // 主键id + ClassName string `json:"class_name"` // 分类名称 + ClassStatus int `json:"class_status"` // 分类状态(1:正常 2:隐藏) + ClassIcon string `json:"class_icon"` // 图标地址 + ClassBrief string `json:"class_brief"` // 分类简介 + Sort uint `json:"sort"` // 排序值(越大排名越靠前) + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 +} + +// GetBaseClassDto 详情-基础数据-分类表 +func GetBaseClassDto(m *model.BaseClass) *BaseClassDto { + return &BaseClassDto{ + ClassId: fmt.Sprintf("%d", m.ClassId), + ClassName: m.ClassName, + ClassStatus: m.ClassStatus, + ClassIcon: utils.AddOssDomain(m.ClassIcon), + ClassBrief: m.ClassBrief, + Sort: m.Sort, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// GetBaseClassListDto 列表-基础数据-分类表 +func GetBaseClassListDto(m []*model.BaseClass) []*BaseClassDto { + // 处理返回值 + responses := make([]*BaseClassDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &BaseClassDto{ + ClassId: fmt.Sprintf("%d", v.ClassId), + ClassName: v.ClassName, + ClassStatus: v.ClassStatus, + ClassIcon: utils.AddOssDomain(v.ClassIcon), + ClassBrief: v.ClassBrief, + Sort: v.Sort, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} diff --git a/api/dto/Coupon.go b/api/dto/Coupon.go new file mode 100644 index 0000000..cf857b2 --- /dev/null +++ b/api/dto/Coupon.go @@ -0,0 +1,101 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +type CouponDto struct { + CouponId string `json:"coupon_id"` // 主键id + CouponName string `json:"coupon_name"` // 优惠券名称 + CouponType int `json:"coupon_type"` // 优惠券类型(1:无门槛 2:满减) + CouponStatus int `json:"coupon_status"` // 状态(1:正常 2:强制失效 3:结束 4:删除) + ApplicationScope int `json:"application_scope"` // 适用范围(1:全场通用) + IsMutex int `json:"is_mutex"` // 是否互斥(0:否 1:是) + CouponCount int `json:"coupon_count"` // 发放数量 + CouponTakeCount int `json:"coupon_take_count"` // 已领取数量 + CouponUsedCount int `json:"coupon_used_count"` // 已使用数量 + CouponPrice float64 `json:"coupon_price"` // 优惠券金额 + WithAmount float64 `json:"with_amount"` // 符合满减标准金额(优惠券类型为满减时使用) + ValidType int `json:"valid_type"` // 有效类型(1:绝对时效,xxx-xxx时间段有效 2:相对时效 n天内有效) + ValidDays int `json:"valid_days"` // 自领取之日起有效天数 + ValidStartTime *model.LocalTime `json:"valid_start_time"` // 开始使用时间 + ValidEndTime *model.LocalTime `json:"valid_end_time"` // 结束使用时间 + CouponDesc string `json:"coupon_desc"` // 优惠券描述 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 +} + +// GetCouponListDto 列表 +func GetCouponListDto(m []*model.Coupon) []*CouponDto { + // 处理返回值 + responses := make([]*CouponDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &CouponDto{ + CouponId: fmt.Sprintf("%d", v.CouponId), + CouponName: v.CouponName, + CouponType: v.CouponType, + CouponStatus: v.CouponStatus, + ApplicationScope: v.ApplicationScope, + IsMutex: v.IsMutex, + CouponCount: v.CouponCount, + CouponTakeCount: v.CouponTakeCount, + CouponUsedCount: v.CouponUsedCount, + CouponPrice: v.CouponPrice, + ValidType: v.ValidType, + ValidStartTime: v.ValidStartTime, + ValidEndTime: v.ValidEndTime, + CouponDesc: v.CouponDesc, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + if v.WithAmount != nil { + response.WithAmount = *v.WithAmount + } + + if v.ValidDays != nil { + response.ValidDays = *v.ValidDays + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetCouponDto 优惠卷详情 +func GetCouponDto(m *model.Coupon) *CouponDto { + response := &CouponDto{ + CouponId: fmt.Sprintf("%d", m.CouponId), + CouponName: m.CouponName, + CouponType: m.CouponType, + CouponStatus: m.CouponStatus, + ApplicationScope: m.ApplicationScope, + IsMutex: m.IsMutex, + CouponCount: m.CouponCount, + CouponTakeCount: m.CouponTakeCount, + CouponUsedCount: m.CouponUsedCount, + CouponPrice: m.CouponPrice, + ValidType: m.ValidType, + ValidStartTime: m.ValidStartTime, + ValidEndTime: m.ValidEndTime, + CouponDesc: m.CouponDesc, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } + + if m.WithAmount != nil { + response.WithAmount = *m.WithAmount + } + + if m.ValidDays != nil { + response.ValidDays = *m.ValidDays + } + + return response +} diff --git a/api/dto/Login.go b/api/dto/Login.go new file mode 100644 index 0000000..4544200 --- /dev/null +++ b/api/dto/Login.go @@ -0,0 +1,68 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" + "time" +) + +type WxDto struct { + UserId string `json:"user_id"` // 用户id + UserName string `json:"user_name"` // 用户名称 + Mobile string `json:"mobile"` // 手机号 + OpenId string `json:"open_id"` // 用户微信标识 + Avatar string `json:"avatar"` // 头像 + IsMember int `json:"is_member"` // 是否会员(0:否 1:是) + MemberExpireDate *time.Time `json:"member_expire_date"` // 会员到期时间(非会员时为null) + Token string `json:"token"` // token +} + +type MobileDto struct { + UserId string `json:"user_id"` // 用户id + UserName string `json:"user_name"` // 用户名称 + Mobile string `json:"mobile"` // 手机号 + OpenId string `json:"open_id"` // 用户微信标识 + Avatar string `json:"avatar"` // 头像 + IsMember int `json:"is_member"` // 是否会员(0:否 1:是) + MemberExpireDate *time.Time `json:"member_expire_date"` // 会员到期时间(非会员时为null) + Token string `json:"token"` // token +} + +// LoginWxDto 微信登陆 +func LoginWxDto(m *model.User) *WxDto { + return &WxDto{ + UserId: fmt.Sprintf("%d", m.UserId), + UserName: m.UserName, + Mobile: m.Mobile, + OpenId: m.OpenId, + Avatar: utils.AddOssDomain(m.Avatar), + IsMember: m.IsMember, + MemberExpireDate: m.MemberExpireDate, + } +} + +// LoginMobileDto 手机号登陆 +func LoginMobileDto(m *model.User) *MobileDto { + return &MobileDto{ + UserId: fmt.Sprintf("%d", m.UserId), + UserName: m.UserName, + Mobile: m.Mobile, + OpenId: m.OpenId, + Avatar: utils.AddOssDomain(m.Avatar), + IsMember: m.IsMember, + MemberExpireDate: m.MemberExpireDate, + } +} + +// LoadToken 加载token +func (r *WxDto) LoadToken(token string) *WxDto { + r.Token = token + return r +} + +// LoadToken 加载token +func (r *MobileDto) LoadToken(token string) *MobileDto { + r.Token = token + return r +} diff --git a/api/dto/OrderMember.go b/api/dto/OrderMember.go new file mode 100644 index 0000000..bc2262f --- /dev/null +++ b/api/dto/OrderMember.go @@ -0,0 +1,126 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" + "time" +) + +// OrderMemberDto 订单-会员 +type OrderMemberDto struct { + OrderId string `json:"order_id"` // 主键id + UserId string `json:"user_id"` // 用户id + SystemMemberId string `json:"system_member_id"` // 会员id + OrderStatus int `json:"order_status"` // 订单状态(1:待支付 2:已完成 3:已取消) + IsDelete int `json:"is_delete"` // 用户删除状态(0:否 1:是) + PayChannel int `json:"pay_channel"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) + PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PayTime *time.Time `json:"pay_time"` // 支付时间 + RefundStatus int `json:"refund_status"` // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + OrderNo string `json:"order_no"` // 系统订单编号 + EscrowTradeNo string `json:"escrow_trade_no"` // 第三方支付流水号 + AmountTotal float64 `json:"amount_total"` // 订单金额 + CouponAmountTotal float64 `json:"coupon_amount_total"` // 优惠卷总金额 + PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 + CancelStatus int `json:"cancel_status"` // 取消状态(0:否 1:是) + CancelTime *time.Time `json:"cancel_time"` // 订单取消时间 + CancelRemarks string `json:"cancel_remarks"` // 取消订单备注 + OrderRemarks string `json:"order_remarks"` // 订单备注 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + SystemMember *SystemMemberDto `json:"system_member"` // 会员 + UserName string `json:"user_name"` // 用户名称 + Avatar string `json:"avatar"` // 用户头像 +} + +// GetOrderMemberListDto 列表 +func GetOrderMemberListDto(m []*model.OrderMember) []*OrderMemberDto { + // 处理返回值 + responses := make([]*OrderMemberDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &OrderMemberDto{ + OrderId: fmt.Sprintf("%d", v.OrderId), + UserId: fmt.Sprintf("%d", v.UserId), + SystemMemberId: fmt.Sprintf("%d", v.SystemMemberId), + OrderStatus: v.OrderStatus, + IsDelete: v.IsDelete, + PayChannel: v.PayChannel, + PayStatus: v.PayStatus, + PayTime: v.PayTime, + RefundStatus: v.RefundStatus, + OrderNo: v.OrderNo, + EscrowTradeNo: v.EscrowTradeNo, + AmountTotal: v.AmountTotal, + CouponAmountTotal: v.CouponAmountTotal, + PaymentAmountTotal: v.PaymentAmountTotal, + CancelStatus: v.CancelStatus, + CancelTime: v.CancelTime, + CancelRemarks: v.CancelRemarks, + OrderRemarks: v.OrderRemarks, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 加载会员数据 + if v.SystemMember != nil { + response = response.LoadSystemMember(v.SystemMember) + } + + // 加载用户数据 + if v.User != nil { + response = response.LoadUserAttr(v.User) + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetOrderMemberDto 详情 +func GetOrderMemberDto(m *model.OrderMember) *OrderMemberDto { + return &OrderMemberDto{ + OrderId: fmt.Sprintf("%d", m.OrderId), + UserId: fmt.Sprintf("%d", m.UserId), + SystemMemberId: fmt.Sprintf("%d", m.SystemMemberId), + OrderStatus: m.OrderStatus, + IsDelete: m.IsDelete, + PayChannel: m.PayChannel, + PayStatus: m.PayStatus, + PayTime: m.PayTime, + RefundStatus: m.RefundStatus, + OrderNo: m.OrderNo, + EscrowTradeNo: m.EscrowTradeNo, + AmountTotal: m.AmountTotal, + CouponAmountTotal: m.CouponAmountTotal, + PaymentAmountTotal: m.PaymentAmountTotal, + CancelStatus: m.CancelStatus, + CancelTime: m.CancelTime, + CancelRemarks: m.CancelRemarks, + OrderRemarks: m.OrderRemarks, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// LoadSystemMember 加载会员数据 +func (r *OrderMemberDto) LoadSystemMember(m *model.SystemMember) *OrderMemberDto { + if m != nil { + r.SystemMember = GetSystemMemberDto(m) + } + return r +} + +// LoadUserAttr 加载用户数据 +func (r *OrderMemberDto) LoadUserAttr(m *model.User) *OrderMemberDto { + if m != nil { + r.UserName = m.UserName + r.Avatar = utils.AddOssDomain(m.Avatar) + } + return r +} diff --git a/api/dto/OrderSingle.go b/api/dto/OrderSingle.go new file mode 100644 index 0000000..f12abe0 --- /dev/null +++ b/api/dto/OrderSingle.go @@ -0,0 +1,139 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" + "time" +) + +// OrderSingleDto 订单-单项 +type OrderSingleDto struct { + OrderId string `json:"order_id"` // 主键id + UserId string `json:"user_id"` // 用户id + QuestionId string `json:"question_id"` // 问题id + OrderStatus int `json:"order_status"` // 订单状态(1:待支付 2:已完成 3:已取消) + IsDelete int `json:"is_delete"` // 用户删除状态(0:否 1:是) + PayChannel int `json:"pay_channel"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) + PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PayTime *time.Time `json:"pay_time"` // 支付时间 + RefundStatus int `json:"refund_status"` // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + OrderNo string `json:"order_no"` // 系统订单编号 + EscrowTradeNo string `json:"escrow_trade_no"` // 第三方支付流水号 + AmountTotal float64 `json:"amount_total"` // 订单金额 + CouponAmountTotal float64 `json:"coupon_amount_total"` // 优惠卷总金额 + PaymentAmountTotal float64 `json:"payment_amount_total"` // 实际付款金额 + CancelStatus int `json:"cancel_status"` // 取消状态(0:否 1:是) + CancelTime *time.Time `json:"cancel_time"` // 订单取消时间 + CancelRemarks string `json:"cancel_remarks"` // 取消订单备注 + OrderRemarks string `json:"order_remarks"` // 订单备注 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + Question *QuestionDto `json:"question"` // 问题 + ValidDate model.LocalTime `json:"valid_date"` // 到期时间 + UserName string `json:"user_name"` // 用户名称 + Avatar string `json:"avatar"` // 用户头像 + IsMember int `json:"is_member"` // 是否会员(0:否 1:是) + MemberExpireDate *time.Time `json:"member_expire_date"` // 用户会员到期时间(非会员时为null) +} + +// GetOrderSingleListDto 列表 +func GetOrderSingleListDto(m []*model.OrderSingle) []*OrderSingleDto { + // 处理返回值 + responses := make([]*OrderSingleDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &OrderSingleDto{ + OrderId: fmt.Sprintf("%d", v.OrderId), + UserId: fmt.Sprintf("%d", v.UserId), + QuestionId: fmt.Sprintf("%d", v.QuestionId), + OrderStatus: v.OrderStatus, + IsDelete: v.IsDelete, + PayChannel: v.PayChannel, + PayStatus: v.PayStatus, + PayTime: v.PayTime, + RefundStatus: v.RefundStatus, + OrderNo: v.OrderNo, + EscrowTradeNo: v.EscrowTradeNo, + AmountTotal: v.AmountTotal, + CouponAmountTotal: v.CouponAmountTotal, + PaymentAmountTotal: v.PaymentAmountTotal, + CancelStatus: v.CancelStatus, + CancelTime: v.CancelTime, + CancelRemarks: v.CancelRemarks, + OrderRemarks: v.OrderRemarks, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 加载问题数据 + if v.Question != nil { + response = response.LoadQuestion(v.Question) + } + + // 加载用户数据 + if v.User != nil { + response = response.LoadUserAttr(v.User) + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetOrderSingleDto 详情 +func GetOrderSingleDto(m *model.OrderSingle) *OrderSingleDto { + return &OrderSingleDto{ + OrderId: fmt.Sprintf("%d", m.OrderId), + UserId: fmt.Sprintf("%d", m.UserId), + QuestionId: fmt.Sprintf("%d", m.QuestionId), + OrderStatus: m.OrderStatus, + IsDelete: m.IsDelete, + PayChannel: m.PayChannel, + PayStatus: m.PayStatus, + PayTime: m.PayTime, + RefundStatus: m.RefundStatus, + OrderNo: m.OrderNo, + EscrowTradeNo: m.EscrowTradeNo, + AmountTotal: m.AmountTotal, + CouponAmountTotal: m.CouponAmountTotal, + PaymentAmountTotal: m.PaymentAmountTotal, + CancelStatus: m.CancelStatus, + CancelTime: m.CancelTime, + CancelRemarks: m.CancelRemarks, + OrderRemarks: m.OrderRemarks, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// LoadQuestion 加载题目数据 +func (r *OrderSingleDto) LoadQuestion(m *model.Question) *OrderSingleDto { + if m != nil { + r.Question = GetQuestionDto(m) + } + return r +} + +// LoadValidDate 加载到期时间 +func (r *OrderSingleDto) LoadValidDate(m *time.Time) *OrderSingleDto { + if m != nil { + r.ValidDate = model.LocalTime(*m) + } + return r +} + +// LoadUserAttr 加载用户数据 +func (r *OrderSingleDto) LoadUserAttr(m *model.User) *OrderSingleDto { + if m != nil { + r.UserName = m.UserName + r.Avatar = utils.AddOssDomain(m.Avatar) + r.IsMember = m.IsMember + r.MemberExpireDate = m.MemberExpireDate + } + return r +} diff --git a/api/dto/Public.go b/api/dto/Public.go new file mode 100644 index 0000000..dd7aaeb --- /dev/null +++ b/api/dto/Public.go @@ -0,0 +1,49 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" +) + +// LoginDto 登陆 +type LoginDto struct { + UserId string `json:"user_id"` // 用户id + NickName string `json:"nick_name"` // 用户名称 + Avatar string `json:"avatar"` // 头像 + Token string `json:"token"` // token +} + +// IndexDto 首页 +type IndexDto struct { + QuestionCount int64 `json:"question_count"` // 问题数量 + UserCount int64 `json:"user_count"` // 用户数量 + ValidMemberCount int64 `json:"valid_member_count"` // 有效会员数 + QuestionSubmitCount int64 `json:"question_submit_count"` // 问题总提交次数 + QuestionPayCount int64 `json:"question_pay_count"` // 问题总支付次数 + MemberBuyCount int64 `json:"member_buy_count"` // 会员购买次数 + MemberAmountTotal float64 `json:"member_amount_total"` // 会员购买总金额 + SingleAmountTotal float64 `json:"single_amount_total"` // 单项购买总金额 + AmountTotal float64 `json:"amount_total"` // 会员+单项购买总金额 +} + +// IndexDataDto 首页动态统计数据 +type IndexDataDto struct { + Date string `json:"date"` // 日期 + Count int64 `json:"count"` // 数量 +} + +// AdminLoginDto 微信登陆 +func AdminLoginDto(m *model.AdminUser) *LoginDto { + return &LoginDto{ + UserId: fmt.Sprintf("%d", m.UserId), + NickName: m.NickName, + Avatar: utils.AddOssDomain(m.Avatar), + } +} + +// LoadToken 加载token +func (r *LoginDto) LoadToken(token string) *LoginDto { + r.Token = token + return r +} diff --git a/api/dto/Question.go b/api/dto/Question.go new file mode 100644 index 0000000..ebc501c --- /dev/null +++ b/api/dto/Question.go @@ -0,0 +1,187 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +// QuestionDto 问题表 +type QuestionDto struct { + QuestionId string `json:"question_id"` // 主键id + QuestionTitle string `json:"question_title"` // 标题 + QuestionSubtitle string `json:"question_subtitle"` // 副标题 + QuestionIden string `json:"question_iden"` // 唯一标识 + QuestionStatus int `json:"question_status"` // 问题状态(1:正常 2:待发布) + IsHide int `json:"is_hide"` // 是否隐藏(0:否 1:是) + IsRecommend int `json:"is_recommend"` // 是否推荐(0:否 1:是) + ClickCount int `json:"click_count"` // 点击次数(点击进入详情页的人次) + SubmitCount int `json:"submit_count"` // 提交次数(提交个人信息进行了算算的人次) + PayCount int `json:"pay_count"` // 支付次数(查看报告的人次) + Price float64 `json:"price"` // 价格(原价) + DiscountPrice *float64 `json:"discount_price"` // 优惠价格 + DiscountEndTime *model.LocalTime `json:"discount_end_time"` // 优惠截止时间 + QuestionBrief string `json:"question_brief"` // 问题介绍 + QuestionExplain string `json:"question_explain"` // 问题解释/科普 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + IsCollection bool `json:"is_collection"` // 用户是否收藏 + FirstTimePrice *float64 `json:"first_time_price"` // 首次购买价格 + BuyCount int `json:"buy_count"` // 被购买数量 + BaseClass []*BaseClassDto `json:"base_class"` // 关联分类 +} + +// GetQuestionDto 详情-问题 +func GetQuestionDto(m *model.Question) *QuestionDto { + return &QuestionDto{ + QuestionId: fmt.Sprintf("%d", m.QuestionId), + QuestionTitle: m.QuestionTitle, + QuestionSubtitle: m.QuestionSubtitle, + QuestionIden: m.QuestionIden, + QuestionStatus: m.QuestionStatus, + IsHide: m.IsHide, + IsRecommend: m.IsRecommend, + ClickCount: m.ClickCount, + SubmitCount: m.SubmitCount, + PayCount: m.PayCount, + Price: m.Price, + DiscountPrice: m.DiscountPrice, + DiscountEndTime: (*model.LocalTime)(m.DiscountEndTime), + QuestionBrief: m.QuestionBrief, + QuestionExplain: m.QuestionExplain, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// GetQuestionListDto 列表-问题 +func GetQuestionListDto(m []*model.Question) []*QuestionDto { + // 处理返回值 + responses := make([]*QuestionDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &QuestionDto{ + QuestionId: fmt.Sprintf("%d", v.QuestionId), + QuestionTitle: v.QuestionTitle, + QuestionSubtitle: v.QuestionSubtitle, + QuestionIden: v.QuestionIden, + QuestionStatus: v.QuestionStatus, + IsHide: v.IsHide, + IsRecommend: v.IsRecommend, + ClickCount: v.ClickCount, + SubmitCount: v.SubmitCount, + PayCount: v.PayCount, + Price: v.Price, + DiscountPrice: v.DiscountPrice, + DiscountEndTime: (*model.LocalTime)(v.DiscountEndTime), + QuestionBrief: v.QuestionBrief, + QuestionExplain: v.QuestionExplain, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetHotQuestionListDto 列表-热榜问题 +func GetHotQuestionListDto(m []*model.Question) []*QuestionDto { + // 处理返回值 + responses := make([]*QuestionDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &QuestionDto{ + QuestionId: fmt.Sprintf("%d", v.QuestionId), + QuestionTitle: v.QuestionTitle, + QuestionSubtitle: v.QuestionSubtitle, + QuestionIden: v.QuestionIden, + ClickCount: v.ClickCount, + SubmitCount: v.SubmitCount, + PayCount: v.PayCount, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetRecommendQuestionListDto 列表-为你推荐 +func GetRecommendQuestionListDto(m []*model.Question) []*QuestionDto { + // 处理返回值 + responses := make([]*QuestionDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &QuestionDto{ + QuestionId: fmt.Sprintf("%d", v.QuestionId), + QuestionTitle: v.QuestionTitle, + QuestionSubtitle: v.QuestionSubtitle, + QuestionIden: v.QuestionIden, + ClickCount: v.ClickCount, + SubmitCount: v.SubmitCount, + PayCount: v.PayCount, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetGuessUserLikeListDto 列表-猜你喜欢 +func GetGuessUserLikeListDto(m []*model.Question) []*QuestionDto { + // 处理返回值 + responses := make([]*QuestionDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &QuestionDto{ + QuestionId: fmt.Sprintf("%d", v.QuestionId), + QuestionTitle: v.QuestionTitle, + QuestionSubtitle: v.QuestionSubtitle, + QuestionIden: v.QuestionIden, + ClickCount: v.ClickCount, + SubmitCount: v.SubmitCount, + PayCount: v.PayCount, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// LoadIsCollection 加载数据-是否收藏 +func (r *QuestionDto) LoadIsCollection(isCollection bool) *QuestionDto { + r.IsCollection = isCollection + + return r +} + +// LoadFirstTimePrice 加载数据-首次购买价格 +func (r *QuestionDto) LoadFirstTimePrice(firstTimePrice *float64) *QuestionDto { + if firstTimePrice != nil { + r.FirstTimePrice = firstTimePrice + } + + return r +} + +// LoadBuyCount 加载数据-问题被购买数量 +func (r *QuestionDto) LoadBuyCount(buyCount int) *QuestionDto { + r.BuyCount = buyCount + + return r +} diff --git a/api/dto/SystemMember.go b/api/dto/SystemMember.go new file mode 100644 index 0000000..4869114 --- /dev/null +++ b/api/dto/SystemMember.go @@ -0,0 +1,55 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +// SystemMemberDto 配置-会员配置 +type SystemMemberDto struct { + SystemMemberId string `json:"system_member_id"` // 主键id + MemberDays uint `json:"member_days"` // 会员天数 + Price float64 `json:"price"` // 价格(原价) + DiscountPrice *float64 `json:"discount_price"` // 优惠价格 + DiscountEndTime *model.LocalTime `json:"discount_end_time"` // 优惠截止时间 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 +} + +// GetSystemMemberListDto 列表 +func GetSystemMemberListDto(m []*model.SystemMember) []*SystemMemberDto { + // 处理返回值 + responses := make([]*SystemMemberDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &SystemMemberDto{ + SystemMemberId: fmt.Sprintf("%d", v.SystemMemberId), + MemberDays: v.MemberDays, + Price: v.Price, + DiscountPrice: v.DiscountPrice, + DiscountEndTime: (*model.LocalTime)(v.DiscountEndTime), + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetSystemMemberDto 详情 +func GetSystemMemberDto(m *model.SystemMember) *SystemMemberDto { + return &SystemMemberDto{ + SystemMemberId: fmt.Sprintf("%d", m.SystemMemberId), + MemberDays: m.MemberDays, + Price: m.Price, + DiscountPrice: m.DiscountPrice, + DiscountEndTime: (*model.LocalTime)(m.DiscountEndTime), + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} diff --git a/api/dto/SystemSingle.go b/api/dto/SystemSingle.go new file mode 100644 index 0000000..156994d --- /dev/null +++ b/api/dto/SystemSingle.go @@ -0,0 +1,49 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +// SystemSingleDto 配置-会员配置 +type SystemSingleDto struct { + SystemSingleId string `json:"system_member_id"` // 主键id + FirstTimePrice float64 `json:"first_time_price"` // 首次购买价格 + ValidDays int `json:"valid_days"` // 购买后有效天数 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 +} + +// GetSystemSingleListDto 列表 +func GetSystemSingleListDto(m []*model.SystemSingle) []*SystemSingleDto { + // 处理返回值 + responses := make([]*SystemSingleDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &SystemSingleDto{ + SystemSingleId: fmt.Sprintf("%d", v.SystemSingleId), + FirstTimePrice: v.FirstTimePrice, + ValidDays: v.ValidDays, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetSystemSingleDto 详情 +func GetSystemSingleDto(m *model.SystemSingle) *SystemSingleDto { + return &SystemSingleDto{ + SystemSingleId: fmt.Sprintf("%d", m.SystemSingleId), + FirstTimePrice: m.FirstTimePrice, + ValidDays: m.ValidDays, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} diff --git a/api/dto/User.go b/api/dto/User.go new file mode 100644 index 0000000..3acae1d --- /dev/null +++ b/api/dto/User.go @@ -0,0 +1,96 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/utils" + "time" +) + +// UserDto 用户表 +type UserDto struct { + UserId string `json:"user_id"` // 用户id + UserName string `json:"user_name"` // 用户名称 + Mobile string `json:"mobile"` // 手机号 + UserStatus int `json:"user_status"` // 状态(1:正常 2:禁用) + RegisterSource int `json:"register_source"` // 注册来源(1:app注册 2:公众号注册) + OpenId string `json:"open_id"` // 用户微信标识 + UnionId string `json:"union_id"` // 微信开放平台标识 + Age *uint `json:"age"` // 年龄 + Sex uint `json:"sex"` // 性别(0:未知 1:男 2:女) + Avatar string `json:"avatar"` // 头像 + IsMember int `json:"is_member"` // 是否会员(0:否 1:是) + MemberExpireDate *time.Time `json:"member_expire_date"` // 会员到期时间(非会员时为null) + LoginAt model.LocalTime `json:"login_at"` // 登陆时间 + LoginIp string `json:"login_ip"` // 登陆ip + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + IsInfoComplete int `json:"is_info_complete"` // 信息完善状态 0:否 1:是 +} + +// GetUserListDto 列表 +func GetUserListDto(m []*model.User) []*UserDto { + // 处理返回值 + responses := make([]*UserDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &UserDto{ + UserId: fmt.Sprintf("%d", v.UserId), + UserName: v.UserName, + Mobile: v.Mobile, + UserStatus: v.UserStatus, + RegisterSource: v.RegisterSource, + Age: v.Age, + Sex: uint(v.Sex), + Avatar: utils.AddOssDomain(v.Avatar), + IsMember: v.IsMember, + MemberExpireDate: v.MemberExpireDate, + LoginAt: v.LoginAt, + LoginIp: v.LoginIp, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 加载信息完善状态 + if v.UserInfo != nil { + response = response.LoadIsInfoComplete(v.UserInfo) + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// GetUserDto 详情-问题 +func GetUserDto(m *model.User) *UserDto { + return &UserDto{ + UserId: fmt.Sprintf("%d", m.UserId), + UserName: m.UserName, + Mobile: m.Mobile, + UserStatus: m.UserStatus, + RegisterSource: m.RegisterSource, + Age: m.Age, + Sex: uint(m.Sex), + Avatar: utils.AddOssDomain(m.Avatar), + IsMember: m.IsMember, + MemberExpireDate: m.MemberExpireDate, + LoginAt: m.LoginAt, + LoginIp: m.LoginIp, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// LoadIsInfoComplete 加载信息完善状态 +func (r *UserDto) LoadIsInfoComplete(m *model.UserInfo) *UserDto { + if m != nil { + if m.DiseaseClassId != 0 && m.FamilyHistoryId != 0 { + r.IsInfoComplete = 1 + } + } + return r +} diff --git a/api/dto/UserCollection.go b/api/dto/UserCollection.go new file mode 100644 index 0000000..bb9f0b5 --- /dev/null +++ b/api/dto/UserCollection.go @@ -0,0 +1,62 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +type UserCollectionDto struct { + CollectionId string `json:"collection_id"` // 主键id + UserId string `json:"user_id"` // 用户id + QuestionId string `json:"question_id"` // 问题id + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + Question *QuestionDto `json:"question"` // 问题 +} + +// GetUserCollectionDto 用户收藏详情 +func GetUserCollectionDto(m *model.UserCollection) *UserCollectionDto { + return &UserCollectionDto{ + CollectionId: fmt.Sprintf("%d", m.CollectionId), + UserId: fmt.Sprintf("%d", m.UserId), + QuestionId: fmt.Sprintf("%d", m.QuestionId), + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// GetUserCollectionListDto 列表 +func GetUserCollectionListDto(m []*model.UserCollection) []*UserCollectionDto { + // 处理返回值 + responses := make([]*UserCollectionDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &UserCollectionDto{ + CollectionId: fmt.Sprintf("%d", v.CollectionId), + UserId: fmt.Sprintf("%d", v.UserId), + QuestionId: fmt.Sprintf("%d", v.QuestionId), + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 加载问题数据 + if v.Question != nil { + response = response.LoadQuestion(v.Question) + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// LoadQuestion 加载题目数据 +func (r *UserCollectionDto) LoadQuestion(m *model.Question) *UserCollectionDto { + if m != nil { + r.Question = GetQuestionDto(m) + } + return r +} diff --git a/api/dto/UserCoupon.go b/api/dto/UserCoupon.go new file mode 100644 index 0000000..7e1da73 --- /dev/null +++ b/api/dto/UserCoupon.go @@ -0,0 +1,79 @@ +package dto + +import ( + "fmt" + "hepa-calc-admin-api/api/model" +) + +type UserCouponDto struct { + UserCouponId string `json:"user_coupon_id"` // 主键id + UserId string `json:"user_id"` // 用户id + CouponId string `json:"coupon_id"` // 优惠券id + UserCouponStatus int `json:"user_coupon_status"` // 状态(0:未使用 1:已使用 3:已过期) + IsWindows int `json:"is_windows"` // 是否已弹窗(0:否 1:是) + CouponUseDate model.LocalTime `json:"coupon_use_date"` // 使用时间 + ValidStartTime model.LocalTime `json:"valid_start_time"` // 有效开始时间 + ValidEndTime model.LocalTime `json:"valid_end_time"` // 过期时间 + CreatedAt model.LocalTime `json:"created_at"` // 创建时间 + UpdatedAt model.LocalTime `json:"updated_at"` // 更新时间 + Coupon *CouponDto `json:"coupon"` // 优惠卷 +} + +// GetUserCouponDto 用户优惠卷详情 +func GetUserCouponDto(m *model.UserCoupon) *UserCouponDto { + return &UserCouponDto{ + UserCouponId: fmt.Sprintf("%d", m.UserCouponId), + UserId: fmt.Sprintf("%d", m.UserId), + CouponId: fmt.Sprintf("%d", m.CouponId), + UserCouponStatus: m.UserCouponStatus, + IsWindows: m.IsWindows, + CouponUseDate: m.CouponUseDate, + ValidStartTime: m.ValidStartTime, + ValidEndTime: m.ValidEndTime, + CreatedAt: m.CreatedAt, + UpdatedAt: m.UpdatedAt, + } +} + +// GetUserCouponListDto 列表 +func GetUserCouponListDto(m []*model.UserCoupon) []*UserCouponDto { + // 处理返回值 + responses := make([]*UserCouponDto, len(m)) + + if len(m) > 0 { + for i, v := range m { + response := &UserCouponDto{ + UserCouponId: fmt.Sprintf("%d", v.UserCouponId), + UserId: fmt.Sprintf("%d", v.UserId), + CouponId: fmt.Sprintf("%d", v.CouponId), + UserCouponStatus: v.UserCouponStatus, + IsWindows: v.IsWindows, + CouponUseDate: v.CouponUseDate, + ValidStartTime: v.ValidStartTime, + ValidEndTime: v.ValidEndTime, + CreatedAt: v.CreatedAt, + UpdatedAt: v.UpdatedAt, + } + + // 加载优惠卷数据 + if v.Coupon != nil { + response = response.LoadCoupon(v.Coupon) + } + + // 将转换后的结构体添加到新切片中 + responses[i] = response + } + } + + return responses +} + +// LoadCoupon 加载优惠卷数据 +func (r *UserCouponDto) LoadCoupon(m *model.Coupon) *UserCouponDto { + if m != nil { + d := GetCouponDto(m) + + r.Coupon = d + } + return r +} diff --git a/api/exception/exception.go b/api/exception/exception.go new file mode 100644 index 0000000..00d52cd --- /dev/null +++ b/api/exception/exception.go @@ -0,0 +1,43 @@ +package exception + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/consts" + "log" + "net/http" + "runtime/debug" +) + +// Recover +// @Description: 处理全局异常 +// @return gin.HandlerFunc +func Recover() gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + if r := recover(); r != nil { + // 打印错误堆栈信息 + log.Printf("panic: %v\n", r) + debug.PrintStack() + c.JSON(http.StatusInternalServerError, gin.H{ + "code": consts.ServerError, + "message": errorToString(r), + "data": "", + }) + // 终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码 + c.Abort() + } + }() + // 加载完 defer recover,继续后续接口调用 + c.Next() + } +} + +// recover错误,转string +func errorToString(r interface{}) string { + switch v := r.(type) { + case error: + return v.Error() + default: + return r.(string) + } +} diff --git a/api/middlewares/auth.go b/api/middlewares/auth.go new file mode 100644 index 0000000..8eb8640 --- /dev/null +++ b/api/middlewares/auth.go @@ -0,0 +1,57 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/responses" +) + +// Auth Auth认证 +func Auth() gin.HandlerFunc { + return func(c *gin.Context) { + // 获取用户id + adminUserId := c.GetInt64("AdminUserId") + if adminUserId == 0 { + responses.Fail(c) + c.Abort() + return + } + + // 获取用户数据 + adminUserDao := dao.AdminUserDao{} + adminUser, err := adminUserDao.GetAdminUserById(adminUserId) + if err != nil || adminUser == nil { + responses.FailWithMessage("用户数据错误", c) + c.Abort() + return + } + + if adminUser.Status == 2 { + responses.FailWithMessage("用户审核中", c) + c.Abort() + return + } + + if adminUser.Status == 3 { + responses.FailWithMessage("用户状态异常", c) + c.Abort() + return + } + + if adminUser.IsDisabled == 1 { + responses.FailWithMessage("用户已被禁用", c) + c.Abort() + return + } + + if adminUser.IsDeleted == 1 { + responses.FailWithMessage("用户状态异常", c) + c.Abort() + return + } + + c.Set("AdminUserId", adminUserId) // 用户id + + c.Next() + } +} diff --git a/api/middlewares/cors.go b/api/middlewares/cors.go new file mode 100644 index 0000000..9ee0ab4 --- /dev/null +++ b/api/middlewares/cors.go @@ -0,0 +1,29 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +// Cors +// @Description: 跨域中间件 +// @return gin.HandlerFunc +func Cors() gin.HandlerFunc { + return func(c *gin.Context) { + method := c.Request.Method + origin := c.Request.Header.Get("Origin") + if origin != "" { + c.Header("Access-Control-Allow-Origin", origin) + c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") + c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") + c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") + c.Header("Access-Control-Allow-Credentials", "false") + c.Set("content-type", "application/json") + } + if method == "OPTIONS" { + c.AbortWithStatus(http.StatusNoContent) + return + } + c.Next() + } +} diff --git a/api/middlewares/jwt.go b/api/middlewares/jwt.go new file mode 100644 index 0000000..09144e5 --- /dev/null +++ b/api/middlewares/jwt.go @@ -0,0 +1,72 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/consts" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "net/http" + "strconv" + "strings" +) + +// Jwt jwt认证 +func Jwt() gin.HandlerFunc { + return func(c *gin.Context) { + authorization := c.Request.Header.Get("Authorization") + if authorization == "" || !strings.HasPrefix(authorization, "Bearer ") { + c.JSON(http.StatusUnauthorized, gin.H{ + "message": "请求未授权", + "code": consts.TokenError, + "data": "", + }) + c.Abort() + return + } + + // 去除Bearer + authorization = authorization[7:] // 截取字符 + + // 检测是否存在黑名单 + res, _ := global.Redis.Get(c, "jwt_black_"+authorization).Result() + if res != "" { + c.JSON(http.StatusOK, gin.H{ + "message": "token错误/过期", + "code": consts.TokenError, + "data": "", + }) + + c.Abort() + return + } + + // 解析jwt + t, err := utils.ParseJwt(authorization) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "message": "token错误/过期", + "code": consts.TokenError, + "data": "", + }) + + c.Abort() + return + } + + // 转换类型 + userId, err := strconv.ParseInt(t.UserId, 10, 64) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "message": "token错误", + "code": consts.TokenError, + "data": "", + }) + + c.Abort() + return + } + + c.Set("AdminUserId", userId) // 用户id + c.Next() + } +} diff --git a/api/middlewares/logrus.go b/api/middlewares/logrus.go new file mode 100644 index 0000000..d084e97 --- /dev/null +++ b/api/middlewares/logrus.go @@ -0,0 +1,60 @@ +package middlewares + +import ( + "fmt" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "hepa-calc-admin-api/global" + "time" +) + +// Logrus 日志中间件 +func Logrus() gin.HandlerFunc { + return func(c *gin.Context) { + + // 开始时间 + startTime := time.Now() + + // 处理请求 + c.Next() + + // 获取 请求 参数 + params := make(map[string]string) + + paramsRaw, ok := c.Get("params") + if ok { + requestParams, ok := paramsRaw.(map[string]string) + if ok || len(requestParams) > 0 { + params = requestParams + } + } + + // 结束时间 + endTime := time.Now() + + // 执行时间 + latencyTime := fmt.Sprintf("%6v", endTime.Sub(startTime)) + + // 请求方式 + reqMethod := c.Request.Method + + // 请求路由 + reqUri := c.Request.RequestURI + + // 状态码 + statusCode := c.Writer.Status() + + // 请求IP + clientIP := c.ClientIP() + + // 日志格式 + global.Logger.WithFields(logrus.Fields{ + "http_status": statusCode, + "total_time": latencyTime, + "ip": clientIP, + "method": reqMethod, + "uri": reqUri, + "params": params, + }).Info("access") + } +} diff --git a/api/middlewares/requestParamsMiddleware.go b/api/middlewares/requestParamsMiddleware.go new file mode 100644 index 0000000..8d67d83 --- /dev/null +++ b/api/middlewares/requestParamsMiddleware.go @@ -0,0 +1,92 @@ +package middlewares + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/consts" + "io" + "net/http" +) + +// RequestParamsMiddleware 获取请求参数中间件 +func RequestParamsMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + contentType := c.Request.Header.Get("Content-Type") + + params := make(map[string]string) + + // 判断请求参数类型 + switch contentType { + case "application/json": + // 解析 application/json 请求体 + bodyBytes, err := io.ReadAll(c.Request.Body) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read request body"}) + c.Abort() + return + } + + // 创建新的请求对象,并设置请求体数据 + c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + + var jsonParams map[string]interface{} + err = json.Unmarshal(bodyBytes, &jsonParams) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Invalid JSON data", + "code": consts.HttpError, + "data": "", + }) + c.Abort() + return + } + + for key, value := range jsonParams { + params[key] = fmt.Sprintf("%v", value) + } + + // 存储参数到上下文 + c.Set("params", params) + + case "multipart/form-data", "application/form-data", "application/x-www-form-urlencoded": + // 解析 Form 表单参数 + err := c.Request.ParseForm() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Invalid form data", + "code": consts.HttpError, + "data": "", + }) + c.Abort() + return + } + + for key, values := range c.Request.Form { + if len(values) > 0 { + params[key] = fmt.Sprintf("%v", values[0]) + } + } + + // 存储参数到上下文 + c.Set("params", params) + + default: + // 解析 URL 参数 + queryParams := c.Request.URL.Query() + + for key, values := range queryParams { + if len(values) > 0 { + params[key] = fmt.Sprintf("%v", values[0]) + } + } + + // 存储参数到上下文 + c.Set("params", params) + } + + // 继续处理请求 + c.Next() + } +} diff --git a/api/model/AdminUser.go b/api/model/AdminUser.go new file mode 100644 index 0000000..bf8c456 --- /dev/null +++ b/api/model/AdminUser.go @@ -0,0 +1,42 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// AdminUser 后台-用户表 +type AdminUser struct { + UserId int64 `gorm:"column:user_id;type:bigint(19);primary_key;comment:主键id" json:"user_id"` + Access string `gorm:"column:access;type:varchar(64);comment:账号;NOT NULL" json:"access"` + Password string `gorm:"column:password;type:varchar(128);comment:密码;NOT NULL" json:"password"` + Salt string `gorm:"column:salt;type:varchar(255);comment:密码掩码;NOT NULL" json:"salt"` + NickName string `gorm:"column:nick_name;type:varchar(255);comment:昵称" json:"nick_name"` + Status int `gorm:"column:status;type:tinyint(1);default:2;comment:状态(1:正常 2:审核中 3:审核失败)" json:"status"` + IsDeleted int `gorm:"column:is_deleted;type:tinyint(1);default:0;comment:是否被删除(0:否 1:是)" json:"is_deleted"` + IsDisabled int `gorm:"column:is_disabled;type:tinyint(1);default:0;comment:是否被禁用(0:否 1:是)" json:"is_disabled"` + Phone string `gorm:"column:phone;type:varchar(11);comment:手机号" json:"phone"` + Avatar string `gorm:"column:avatar;type:varchar(255);comment:头像" json:"avatar"` + Sex int `gorm:"column:sex;type:tinyint(1);comment:性别(1:男 2:女)" json:"sex"` + Email string `gorm:"column:email;type:varchar(100);comment:邮箱" json:"email"` + Model +} + +func (m *AdminUser) TableName() string { + return "admin_user" +} + +func (m *AdminUser) BeforeCreate(tx *gorm.DB) error { + if m.UserId == 0 { + m.UserId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/BaseAgreement.go b/api/model/BaseAgreement.go new file mode 100644 index 0000000..dc19c6c --- /dev/null +++ b/api/model/BaseAgreement.go @@ -0,0 +1,33 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// BaseAgreement 基础数据-协议 +type BaseAgreement struct { + AgreementId int64 `gorm:"column:agreement_id;type:bigint(19);primary_key;comment:主键id" json:"agreement_id"` + AgreementTitle string `gorm:"column:agreement_title;type:varchar(100);comment:协议标题" json:"agreement_title"` + AgreementContent string `gorm:"column:agreement_content;type:text;comment:协议内容" json:"agreement_content"` + Model +} + +func (m *BaseAgreement) TableName() string { + return "base_agreement" +} + +func (m *BaseAgreement) BeforeCreate(tx *gorm.DB) error { + if m.AgreementId == 0 { + m.AgreementId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/BaseClass.go b/api/model/BaseClass.go new file mode 100644 index 0000000..2f5a5c6 --- /dev/null +++ b/api/model/BaseClass.go @@ -0,0 +1,36 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// BaseClass 基础数据-分类表 +type BaseClass struct { + ClassId int64 `gorm:"column:class_id;type:bigint(19);primary_key;comment:主键id" json:"class_id"` + ClassName string `gorm:"column:class_name;type:varchar(100);comment:分类名称" json:"class_name"` + ClassStatus int `gorm:"column:class_status;type:tinyint(1);default:1;comment:分类状态(1:正常 2:隐藏)" json:"class_status"` + ClassIcon string `gorm:"column:class_icon;type:varchar(255);comment:图标地址" json:"class_icon"` + ClassBrief string `gorm:"column:class_brief;type:text;comment:分类简介" json:"class_brief"` + Sort uint `gorm:"column:sort;type:int(10) unsigned;default:1;comment:排序值(越大排名越靠前)" json:"sort"` + Model +} + +func (m *BaseClass) TableName() string { + return "base_class" +} + +func (m *BaseClass) BeforeCreate(tx *gorm.DB) error { + if m.ClassId == 0 { + m.ClassId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/Coupon.go b/api/model/Coupon.go new file mode 100644 index 0000000..420a6ca --- /dev/null +++ b/api/model/Coupon.go @@ -0,0 +1,47 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +type Coupon struct { + CouponId int64 `gorm:"column:coupon_id;type:bigint(19);primary_key;comment:主键id" json:"coupon_id"` + CouponName string `gorm:"column:coupon_name;type:varchar(255);comment:优惠卷名称" json:"coupon_name"` + CouponType int `gorm:"column:coupon_type;type:varchar(255);comment:优惠卷类型(1:无门槛 2:满减)" json:"coupon_type"` + CouponStatus int `gorm:"column:coupon_status;type:tinyint(1);default:1;comment:状态(1:正常 2:强制失效 3:结束 4:删除)" json:"coupon_status"` + ApplicationScope int `gorm:"column:application_scope;type:tinyint(1);default:1;comment:适用范围(1:全场通用 2:单项 3:会员)" json:"application_scope"` + IsMutex int `gorm:"column:is_mutex;type:tinyint(1);default:1;comment:是否互斥(0:否 1:是)互斥情况下无法和其他优惠卷同时使用" json:"is_mutex"` + CouponCount int `gorm:"column:coupon_count;type:int(10);default:1;comment:发放数量;NOT NULL" json:"coupon_count"` + CouponTakeCount int `gorm:"column:coupon_take_count;type:int(10);comment:已领取数量" json:"coupon_take_count"` + CouponUsedCount int `gorm:"column:coupon_used_count;type:int(10);default:0;comment:已使用数量" json:"coupon_used_count"` + CouponPrice float64 `gorm:"column:coupon_price;type:decimal(10,2) unsigned;default:0.00;comment:优惠卷金额" json:"coupon_price"` + WithAmount *float64 `gorm:"column:with_amount;type:decimal(10,2);comment:符合满减标准金额(优惠卷类型为满减时使用)" json:"with_amount"` + ValidType int `gorm:"column:valid_type;type:tinyint(1);comment:有效类型(1:绝对时效,xxx-xxx时间段有效 2:相对时效 n天内有效);NOT NULL" json:"valid_type"` + ValidDays *int `gorm:"column:valid_days;type:int(3);comment:自领取之日起有效天数" json:"valid_days"` + ValidStartTime *LocalTime `gorm:"column:valid_start_time;type:datetime;comment:开始使用时间" json:"valid_start_time"` + ValidEndTime *LocalTime `gorm:"column:valid_end_time;type:datetime;comment:结束使用时间" json:"valid_end_time"` + QuestionId *int64 `gorm:"column:question_id;type:bigint(19);comment:问题id(适用范围为单项时生效,如果此项为null,则表示所有单项通用)" json:"question_id"` + SystemMemberId *int64 `gorm:"column:system_member_id;type:bigint(19);comment:会员id(适用范围为会员时生效,如果此项为null,则表示所有会员通用)" json:"system_member_id"` + CouponDesc string `gorm:"column:coupon_desc;type:varchar(200);comment:优惠卷描述" json:"coupon_desc"` + Model +} + +func (m *Coupon) TableName() string { + return "coupon" +} + +func (m *Coupon) BeforeCreate(tx *gorm.DB) error { + if m.CouponId == 0 { + m.CouponId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderMember.go b/api/model/OrderMember.go new file mode 100644 index 0000000..51597c2 --- /dev/null +++ b/api/model/OrderMember.go @@ -0,0 +1,50 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderMember 订单-单项 +type OrderMember struct { + OrderId int64 `gorm:"column:order_id;type:bigint(19);primary_key;comment:主键id" json:"order_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);comment:会员id;NOT NULL" json:"system_member_id"` + OrderStatus int `gorm:"column:order_status;type:tinyint(1);default:1;comment:订单状态(1:待支付 2:已完成 3:已取消)" json:"order_status"` + IsDelete int `gorm:"column:is_delete;type:tinyint(1);default:0;comment:用户删除状态(0:否 1:是)" json:"is_delete"` + PayChannel int `gorm:"column:pay_channel;type:tinyint(1);comment:支付渠道(1:h5支付 2:app支付 3:会员支付);NOT NULL" json:"pay_channel"` + PayStatus int `gorm:"column:pay_status;type:tinyint(1);default:1;comment:支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款);NOT NULL" json:"pay_status"` + PayTime *time.Time `gorm:"column:pay_time;type:datetime;comment:支付时间" json:"pay_time"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款);NOT NULL" json:"refund_status"` + OrderNo string `gorm:"column:order_no;type:varchar(30);comment:系统订单编号;NOT NULL" json:"order_no"` + EscrowTradeNo string `gorm:"column:escrow_trade_no;type:varchar(100);comment:第三方支付流水号;NOT NULL" json:"escrow_trade_no"` + AmountTotal float64 `gorm:"column:amount_total;type:decimal(10,2) unsigned;default:0.00;comment:订单金额" json:"amount_total"` + CouponAmountTotal float64 `gorm:"column:coupon_amount_total;type:decimal(10,2);default:0.00;comment:优惠卷总金额" json:"coupon_amount_total"` + PaymentAmountTotal float64 `gorm:"column:payment_amount_total;type:decimal(10,2);default:0.00;comment:实际付款金额" json:"payment_amount_total"` + CancelStatus int `gorm:"column:cancel_status;type:tinyint(1);default:0;comment:取消状态(0:否 1:是)" json:"cancel_status"` + CancelTime *time.Time `gorm:"column:cancel_time;type:datetime;comment:订单取消时间" json:"cancel_time"` + CancelRemarks string `gorm:"column:cancel_remarks;type:varchar(255);comment:取消订单备注" json:"cancel_remarks"` + OrderRemarks string `gorm:"column:order_remarks;type:varchar(255);comment:订单备注" json:"order_remarks"` + Model + SystemMember *SystemMember `gorm:"foreignKey:SystemMemberId;references:system_member_id" json:"system_member"` + User *User `gorm:"foreignKey:UserId;references:user_id" json:"user"` +} + +func (m *OrderMember) TableName() string { + return "order_member" +} + +func (m *OrderMember) BeforeCreate(tx *gorm.DB) error { + if m.OrderId == 0 { + m.OrderId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderMemberCoupon.go b/api/model/OrderMemberCoupon.go new file mode 100644 index 0000000..90bc4be --- /dev/null +++ b/api/model/OrderMemberCoupon.go @@ -0,0 +1,35 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderMemberCoupon 订单-单项-优惠卷表 +type OrderMemberCoupon struct { + OrderCouponId int64 `gorm:"column:order_coupon_id;type:bigint(19);primary_key;comment:主键id" json:"order_coupon_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:单项订单id;NOT NULL" json:"order_id"` + UserCouponId int64 `gorm:"column:user_coupon_id;type:bigint(19);comment:用户优惠卷表id;NOT NULL" json:"user_coupon_id"` + CouponName string `gorm:"column:coupon_name;type:varchar(255);comment:优惠卷名称" json:"coupon_name"` + CouponUsePrice float64 `gorm:"column:coupon_use_price;type:decimal(10,2) unsigned;default:0.00;comment:优惠卷使用金额" json:"coupon_use_price"` + Model +} + +func (m *OrderMemberCoupon) TableName() string { + return "order_member_coupon" +} + +func (m *OrderMemberCoupon) BeforeCreate(tx *gorm.DB) error { + if m.OrderCouponId == 0 { + m.OrderCouponId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderMemberRefund.go b/api/model/OrderMemberRefund.go new file mode 100644 index 0000000..1f4aee8 --- /dev/null +++ b/api/model/OrderMemberRefund.go @@ -0,0 +1,39 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderMemberRefund 订单-会员-退款表 +type OrderMemberRefund struct { + OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` + OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` + RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` + RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` + RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` + RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` + SuccessTime LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` + Model +} + +func (m *OrderMemberRefund) TableName() string { + return "order_member_refund" +} +func (m *OrderMemberRefund) BeforeCreate(tx *gorm.DB) error { + if m.OrderRefundId == 0 { + m.OrderRefundId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderSingle.go b/api/model/OrderSingle.go new file mode 100644 index 0000000..8b7cffd --- /dev/null +++ b/api/model/OrderSingle.go @@ -0,0 +1,50 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderSingle 订单-单项 +type OrderSingle struct { + OrderId int64 `gorm:"column:order_id;type:bigint(19);primary_key;comment:主键id" json:"order_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + QuestionId int64 `gorm:"column:question_id;type:bigint(19);comment:问题id;NOT NULL" json:"question_id"` + OrderStatus int `gorm:"column:order_status;type:tinyint(1);default:1;comment:订单状态(1:待支付 2:已完成 3:已取消)" json:"order_status"` + IsDelete int `gorm:"column:is_delete;type:tinyint(1);default:0;comment:用户删除状态(0:否 1:是)" json:"is_delete"` + PayChannel int `gorm:"column:pay_channel;type:tinyint(1);comment:支付渠道(1:h5支付 2:app支付 3:会员支付);NOT NULL" json:"pay_channel"` + PayStatus int `gorm:"column:pay_status;type:tinyint(1);default:1;comment:支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款);NOT NULL" json:"pay_status"` + PayTime *time.Time `gorm:"column:pay_time;type:datetime;comment:支付时间" json:"pay_time"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款);NOT NULL" json:"refund_status"` + OrderNo string `gorm:"column:order_no;type:varchar(30);comment:系统订单编号;NOT NULL" json:"order_no"` + EscrowTradeNo string `gorm:"column:escrow_trade_no;type:varchar(100);comment:第三方支付流水号;NOT NULL" json:"escrow_trade_no"` + AmountTotal float64 `gorm:"column:amount_total;type:decimal(10,2) unsigned;default:0.00;comment:订单金额" json:"amount_total"` + CouponAmountTotal float64 `gorm:"column:coupon_amount_total;type:decimal(10,2);default:0.00;comment:优惠卷总金额" json:"coupon_amount_total"` + PaymentAmountTotal float64 `gorm:"column:payment_amount_total;type:decimal(10,2);default:0.00;comment:实际付款金额" json:"payment_amount_total"` + CancelStatus int `gorm:"column:cancel_status;type:tinyint(1);default:0;comment:取消状态(0:否 1:是)" json:"cancel_status"` + CancelTime *time.Time `gorm:"column:cancel_time;type:datetime;comment:订单取消时间" json:"cancel_time"` + CancelRemarks string `gorm:"column:cancel_remarks;type:varchar(255);comment:取消订单备注" json:"cancel_remarks"` + OrderRemarks string `gorm:"column:order_remarks;type:varchar(255);comment:订单备注" json:"order_remarks"` + Model + Question *Question `gorm:"foreignKey:QuestionId;references:question_id" json:"question"` + User *User `gorm:"foreignKey:UserId;references:user_id" json:"user"` +} + +func (m *OrderSingle) TableName() string { + return "order_single" +} + +func (m *OrderSingle) BeforeCreate(tx *gorm.DB) error { + if m.OrderId == 0 { + m.OrderId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderSingleCoupon.go b/api/model/OrderSingleCoupon.go new file mode 100644 index 0000000..39441e3 --- /dev/null +++ b/api/model/OrderSingleCoupon.go @@ -0,0 +1,35 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderSingleCoupon 订单-单项-优惠卷表 +type OrderSingleCoupon struct { + OrderCouponId int64 `gorm:"column:order_coupon_id;type:bigint(19);primary_key;comment:主键id" json:"order_coupon_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:单项订单id;NOT NULL" json:"order_id"` + UserCouponId int64 `gorm:"column:user_coupon_id;type:bigint(19);comment:用户优惠卷表id;NOT NULL" json:"user_coupon_id"` + CouponName string `gorm:"column:coupon_name;type:varchar(255);comment:优惠卷名称" json:"coupon_name"` + CouponUsePrice float64 `gorm:"column:coupon_use_price;type:decimal(10,2) unsigned;default:0.00;comment:优惠卷使用金额" json:"coupon_use_price"` + Model +} + +func (m *OrderSingleCoupon) TableName() string { + return "order_single_coupon" +} + +func (m *OrderSingleCoupon) BeforeCreate(tx *gorm.DB) error { + if m.OrderCouponId == 0 { + m.OrderCouponId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/OrderSingleRefund.go b/api/model/OrderSingleRefund.go new file mode 100644 index 0000000..29f95cf --- /dev/null +++ b/api/model/OrderSingleRefund.go @@ -0,0 +1,40 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// OrderSingleRefund 订单-单项-退款表 +type OrderSingleRefund struct { + OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` + OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` + RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` + RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` + RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` + RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` + SuccessTime LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` + Model +} + +func (m *OrderSingleRefund) TableName() string { + return "order_single_refund" +} + +func (m *OrderSingleRefund) BeforeCreate(tx *gorm.DB) error { + if m.OrderRefundId == 0 { + m.OrderRefundId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/Question.go b/api/model/Question.go new file mode 100644 index 0000000..b4edc50 --- /dev/null +++ b/api/model/Question.go @@ -0,0 +1,45 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// Question 问题表 +type Question struct { + QuestionId int64 `gorm:"column:question_id;type:bigint(19);primary_key;comment:主键id" json:"question_id"` + QuestionTitle string `gorm:"column:question_title;type:varchar(200);comment:标题" json:"question_title"` + QuestionSubtitle string `gorm:"column:question_subtitle;type:varchar(200);comment:副标题" json:"question_subtitle"` + QuestionIden string `gorm:"column:question_iden;type:varchar(255);comment:唯一标识(用于和前端对应)" json:"question_iden"` + QuestionStatus int `gorm:"column:question_status;type:tinyint(1);default:2;comment:问题状态(1:正常 2:待发布)" json:"question_status"` + IsHide int `gorm:"column:is_hide;type:tinyint(1);default:0;comment:是否隐藏(0:否 1:是)" json:"is_hide"` + IsRecommend int `gorm:"column:is_recommend;type:tinyint(1);default:0;comment:是否推荐(0:否 1:是)" json:"is_recommend"` + ClickCount int `gorm:"column:click_count;type:int(5);default:0;comment:点击次数(点击进入详情页的人次)" json:"click_count"` + SubmitCount int `gorm:"column:submit_count;type:int(5);default:0;comment:提交次数(提交个人信息进行了算算的人次)" json:"submit_count"` + PayCount int `gorm:"column:pay_count;type:int(5);default:0;comment:支付次数(查看报告的人次)" json:"pay_count"` + Price float64 `gorm:"column:price;type:decimal(10,2) unsigned;default:0.00;comment:价格(原价)" json:"price"` + DiscountPrice *float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` + DiscountEndTime *time.Time `gorm:"column:discount_end_time;type:datetime;comment:优惠截止时间" json:"discount_end_time"` + QuestionBrief string `gorm:"column:question_brief;type:text;comment:问题介绍" json:"question_brief"` + QuestionExplain string `gorm:"column:question_explain;type:text;comment:问题解释/科普" json:"question_explain"` + Model +} + +func (m *Question) TableName() string { + return "question" +} + +func (m *Question) BeforeCreate(tx *gorm.DB) error { + if m.QuestionId == 0 { + m.QuestionId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/QuestionClass.go b/api/model/QuestionClass.go new file mode 100644 index 0000000..b8e478f --- /dev/null +++ b/api/model/QuestionClass.go @@ -0,0 +1,33 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// QuestionClass 中间表-问题/分类 +type QuestionClass struct { + QuestionClassId int64 `gorm:"column:question_class_id;type:bigint(19);primary_key;comment:主键id" json:"question_class_id"` + QuestionId int64 `gorm:"column:question_id;type:bigint(19);comment:问题id;NOT NULL" json:"question_id"` + ClassId int64 `gorm:"column:class_id;type:bigint(19);comment:分类id;NOT NULL" json:"class_id"` + Model +} + +func (m *QuestionClass) TableName() string { + return "question_class" +} + +func (m *QuestionClass) BeforeCreate(tx *gorm.DB) error { + if m.QuestionClassId == 0 { + m.QuestionClassId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/SystemMember.go b/api/model/SystemMember.go new file mode 100644 index 0000000..254dde9 --- /dev/null +++ b/api/model/SystemMember.go @@ -0,0 +1,35 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// SystemMember 配置-会员配置 +type SystemMember struct { + SystemMemberId int64 `gorm:"column:system_member_id;type:bigint(19);primary_key;comment:主键id" json:"system_member_id"` + MemberDays uint `gorm:"column:member_days;type:int(10) unsigned;default:0;comment:会员天数" json:"member_days"` + Price float64 `gorm:"column:price;type:decimal(10,2);default:0.00;comment:价格(原价)" json:"price"` + DiscountPrice *float64 `gorm:"column:discount_price;type:decimal(10,2);comment:优惠价格" json:"discount_price"` + DiscountEndTime *time.Time `gorm:"column:discount_end_time;type:datetime;comment:优惠截止时间" json:"discount_end_time"` + Model +} + +func (m *SystemMember) TableName() string { + return "system_member" +} + +func (m *SystemMember) BeforeCreate(tx *gorm.DB) error { + if m.SystemMemberId == 0 { + m.SystemMemberId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/SystemSingle.go b/api/model/SystemSingle.go new file mode 100644 index 0000000..56386fd --- /dev/null +++ b/api/model/SystemSingle.go @@ -0,0 +1,33 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// SystemSingle 配置-单项配置 +type SystemSingle struct { + SystemSingleId int64 `gorm:"column:system_single_id;type:bigint(19);primary_key;comment:主键id" json:"system_single_id"` + FirstTimePrice float64 `gorm:"column:first_time_price;type:decimal(10,2);default:0.00;comment:首次购买价格" json:"first_time_price"` + ValidDays int `gorm:"column:valid_days;type:int(5);default:1;comment:购买后有效天数" json:"valid_days"` + Model +} + +func (m *SystemSingle) TableName() string { + return "system_single" +} + +func (m *SystemSingle) BeforeCreate(tx *gorm.DB) error { + if m.SystemSingleId == 0 { + m.SystemSingleId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/User.go b/api/model/User.go new file mode 100644 index 0000000..3c3d060 --- /dev/null +++ b/api/model/User.go @@ -0,0 +1,48 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// User 用户表 +type User struct { + UserId int64 `gorm:"column:user_id;type:bigint(19);primary_key;comment:用户id" json:"user_id"` + UserName string `gorm:"column:user_name;type:varchar(200);comment:用户名称" json:"user_name"` + Mobile string `gorm:"column:mobile;type:varchar(20);comment:手机号;NOT NULL" json:"mobile"` + UserStatus int `gorm:"column:user_status;type:tinyint(1);default:1;comment:状态(1:正常 2:禁用)" json:"user_status"` + RegisterSource int `gorm:"column:register_source;type:tinyint(1);default:1;comment:注册来源(1:app注册 2:公众号注册)" json:"register_source"` + OpenId string `gorm:"column:open_id;type:varchar(100);comment:用户微信标识" json:"open_id"` + UnionId string `gorm:"column:union_id;type:varchar(100);comment:微信开放平台标识" json:"union_id"` + Age *uint `gorm:"column:age;type:int(10) unsigned;comment:年龄" json:"age"` + Sex int `gorm:"column:sex;type:tinyint(1) unsigned;default:0;comment:性别(0:未知 1:男 2:女)" json:"sex"` + Avatar string `gorm:"column:avatar;type:varchar(255);comment:头像" json:"avatar"` + IsMember int `gorm:"column:is_member;type:tinyint(1);default:0;comment:是否会员(0:否 1:是)" json:"is_member"` + MemberExpireDate *time.Time `gorm:"column:member_expire_date;type:datetime;comment:会员到期时间(非会员时为null)" json:"member_expire_date"` + SingleSubmitCount int `gorm:"column:single_submit_count;type:int(5);default:0;comment:单项提交次数(提交个人信息进行了算算的人次)" json:"single_submit_count"` + SinglePayCount int `gorm:"column:single_pay_count;type:int(5);default:0;comment:单项支付次数(查看报告的人次)" json:"single_pay_count"` + MemberBuyCount int `gorm:"column:member_buy_count;type:int(5);default:0;comment:会员购买次数" json:"member_buy_count"` + LoginAt LocalTime `gorm:"column:login_at;type:datetime;comment:登陆时间" json:"login_at"` + LoginIp string `gorm:"column:login_ip;type:varchar(255);comment:登陆ip" json:"login_ip"` + Model + UserInfo *UserInfo `gorm:"foreignKey:UserId;references:user_id" json:"user_info"` +} + +func (m *User) TableName() string { + return "user" +} + +func (m *User) BeforeCreate(tx *gorm.DB) error { + if m.UserId == 0 { + m.UserId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/UserCollection.go b/api/model/UserCollection.go new file mode 100644 index 0000000..53ca596 --- /dev/null +++ b/api/model/UserCollection.go @@ -0,0 +1,34 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// UserCollection 用户收藏表 +type UserCollection struct { + CollectionId int64 `gorm:"column:collection_id;type:bigint(19);primary_key;comment:主键id" json:"collection_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id" json:"user_id"` + QuestionId int64 `gorm:"column:question_id;type:bigint(19);comment:问题id" json:"question_id"` + Model + Question *Question `gorm:"foreignKey:QuestionId;references:question_id" json:"question"` +} + +func (m *UserCollection) TableName() string { + return "user_collection" +} + +func (m *UserCollection) BeforeCreate(tx *gorm.DB) error { + if m.CollectionId == 0 { + m.CollectionId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/UserCoupon.go b/api/model/UserCoupon.go new file mode 100644 index 0000000..91c8557 --- /dev/null +++ b/api/model/UserCoupon.go @@ -0,0 +1,38 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +type UserCoupon struct { + UserCouponId int64 `gorm:"column:user_coupon_id;type:bigint(19);primary_key;comment:主键id" json:"user_coupon_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + CouponId int64 `gorm:"column:coupon_id;type:bigint(19);comment:优惠卷id;NOT NULL" json:"coupon_id"` + UserCouponStatus int `gorm:"column:user_coupon_status;type:tinyint(1);default:0;comment:状态(0:未使用 1:已使用 3:已过期)" json:"user_coupon_status"` + IsWindows int `gorm:"column:is_windows;type:tinyint(1);default:0;comment:是否已弹窗(0:否 1:是)" json:"is_windows"` + CouponUseDate LocalTime `gorm:"column:coupon_use_date;type:datetime;comment:使用时间" json:"coupon_use_date"` + ValidStartTime LocalTime `gorm:"column:valid_start_time;type:datetime;comment:有效使用时间" json:"valid_start_time"` + ValidEndTime LocalTime `gorm:"column:valid_end_time;type:datetime;comment:过期使用时间" json:"valid_end_time"` + Model + Coupon *Coupon `gorm:"foreignKey:CouponId;references:coupon_id" json:"coupon"` +} + +func (m *UserCoupon) TableName() string { + return "user_coupon" +} + +func (m *UserCoupon) BeforeCreate(tx *gorm.DB) error { + if m.UserCouponId == 0 { + m.UserCouponId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/UserInfo.go b/api/model/UserInfo.go new file mode 100644 index 0000000..8918cc7 --- /dev/null +++ b/api/model/UserInfo.go @@ -0,0 +1,43 @@ +package model + +import ( + "gorm.io/gorm" + "hepa-calc-admin-api/global" + "time" +) + +// UserInfo 用户表-基础信息 +type UserInfo struct { + UserInfoId int64 `gorm:"column:user_info_id;type:bigint(19);primary_key;comment:主键id" json:"user_info_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id" json:"user_id"` + Height string `gorm:"column:height;type:varchar(20);comment:身高(cm)" json:"height"` + Weight string `gorm:"column:weight;type:varchar(20);comment:体重(kg)" json:"weight"` + NationId int64 `gorm:"column:nation_id;type:bigint(19);comment:民族id" json:"nation_id"` + FamilyHistoryId int64 `gorm:"column:family_history_id;type:bigint(19);comment:家族病史id" json:"family_history_id"` + ProvinceId int `gorm:"column:province_id;type:int(11);comment:省份id" json:"province_id"` + Province string `gorm:"column:province;type:varchar(40);comment:省份" json:"province"` + CityId int `gorm:"column:city_id;type:int(11);comment:城市id" json:"city_id"` + City string `gorm:"column:city;type:varchar(50);comment:城市" json:"city"` + CountyId int `gorm:"column:county_id;type:int(11);comment:区县id" json:"county_id"` + County string `gorm:"column:county;type:varchar(255);comment:区县" json:"county"` + DiseaseClassId int64 `gorm:"column:disease_class_id;type:bigint(19);comment:疾病分类id" json:"disease_class_id"` + Model +} + +func (m *UserInfo) TableName() string { + return "user_info" +} + +func (m *UserInfo) BeforeCreate(tx *gorm.DB) error { + if m.UserInfoId == 0 { + m.UserInfoId = global.Snowflake.Generate().Int64() + } + + m.CreatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("CreatedAt", m.CreatedAt) + + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} diff --git a/api/model/model.go b/api/model/model.go new file mode 100644 index 0000000..58cffea --- /dev/null +++ b/api/model/model.go @@ -0,0 +1,87 @@ +package model + +import ( + "database/sql/driver" + "errors" + "fmt" + "gorm.io/gorm" + "strings" + "time" +) + +type Model struct { + CreatedAt LocalTime `gorm:"column:created_at;type:datetime;comment:创建时间" json:"created_at"` + UpdatedAt LocalTime `gorm:"column:updated_at;type:datetime;comment:修改时间" json:"updated_at"` +} + +// LocalTime 自定义数据类型 +type LocalTime time.Time + +func (t *LocalTime) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + return nil + } + var err error + // 前端接收的时间字符串 + str := string(data) + // 去除接收的str收尾多余的" + timeStr := strings.Trim(str, "\"") + t1, err := time.Parse("2006-01-02 15:04:05", timeStr) + *t = LocalTime(t1) + return err +} + +func (t LocalTime) MarshalJSON() ([]byte, error) { + formatted := fmt.Sprintf("\"%v\"", time.Time(t).Format("2006-01-02 15:04:05")) + return []byte(formatted), nil +} + +func (t LocalTime) Value() (driver.Value, error) { + // MyTime 转换成 time.Time 类型 + tTime := time.Time(t) + return tTime.Format("2006-01-02 15:04:05"), nil +} + +func (t *LocalTime) Scan(v interface{}) error { + switch vt := v.(type) { + case time.Time: + // 字符串转成 time.Time 类型 + *t = LocalTime(vt) + default: + return errors.New("类型处理错误") + } + return nil +} + +func (t *LocalTime) String() string { + return fmt.Sprintf("hhh:%s", time.Time(*t).String()) +} + +func (t *LocalTime) IsEmpty() bool { + return time.Time(*t).IsZero() +} + +func (m *Model) BeforeUpdate(tx *gorm.DB) (err error) { + m.UpdatedAt = LocalTime(time.Now()) + tx.Statement.SetColumn("UpdatedAt", m.UpdatedAt) + + return nil +} + +func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + if page <= 0 { + page = 1 + } + + switch { + case pageSize > 100: + pageSize = 100 + case pageSize <= 0: + pageSize = 10 + } + + offset := (page - 1) * pageSize + return db.Offset(offset).Limit(pageSize) + } +} diff --git a/api/requests/BaseAgreement.go b/api/requests/BaseAgreement.go new file mode 100644 index 0000000..a8b1d33 --- /dev/null +++ b/api/requests/BaseAgreement.go @@ -0,0 +1,25 @@ +package requests + +type BaseAgreementRequest struct { + GetBaseAgreementPage // 获取协议列表-分页 + PutBaseAgreement // 修改协议 + AddBaseAgreement // 新增协议 +} + +// GetBaseAgreementPage 获取协议列表-分页 +type GetBaseAgreementPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` +} + +// PutBaseAgreement 修改协议 +type PutBaseAgreement struct { + AgreementTitle string `json:"agreement_title" form:"agreement_title" label:"协议标题"` + AgreementContent string `json:"agreement_content" form:"agreement_content" label:"协议内容"` +} + +// AddBaseAgreement 新增协议 +type AddBaseAgreement struct { + AgreementTitle string `json:"agreement_title" form:"agreement_title" label:"协议标题"` + AgreementContent string `json:"agreement_content" form:"agreement_content" label:"协议内容"` +} diff --git a/api/requests/BaseClass.go b/api/requests/BaseClass.go new file mode 100644 index 0000000..1554d65 --- /dev/null +++ b/api/requests/BaseClass.go @@ -0,0 +1,56 @@ +package requests + +type BaseClassRequest struct { + GetBaseClassPage // 获取基础分类列表-分页 + GetBaseClassList // 获取基础分类列表 + PutBaseClassStatus // 操作基础分类状态 + PutBaseClass // 修改基础分类 + AddBaseClass // 新增基础分类 +} + +// GetBaseClassPage 获取基础分类列表-分页 +type GetBaseClassPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + ClassName string `json:"class_name" form:"class_name" label:"分类名称"` + ClassStatus *int `json:"class_status" form:"class_status" label:"分类状态"` // (1:正常 2:隐藏) + Order *GetBaseClassPageOrder `json:"order" form:"order" label:"排序"` +} + +// GetBaseClassPageOrder 获取基础分类列表-分页-排序条件 +type GetBaseClassPageOrder struct { + Sort string `json:"sort" form:"sort" label:"排序"` // 排序值 +} + +// GetBaseClassList 获取基础分类列表 +type GetBaseClassList struct { + ClassName string `json:"class_name" form:"class_name" label:"分类名称"` + ClassStatus *int `json:"class_status" form:"class_status" label:"分类状态"` // (1:正常 2:隐藏) + Order *GetBaseClassListOrder `json:"order" form:"order" label:"排序"` +} + +// GetBaseClassListOrder 获取基础分类列表-排序条件 +type GetBaseClassListOrder struct { + Sort string `json:"sort" form:"sort" label:"排序"` // 排序值 +} + +// PutBaseClassStatus 操作基础分类状态 +type PutBaseClassStatus struct { + ClassStatus int `json:"class_status" form:"class_status" label:"分类状态" validate:"required,oneof=1 2"` // (1:正常 2:隐藏) +} + +// PutBaseClass 修改基础分类 +type PutBaseClass struct { + ClassName string `json:"class_name" form:"class_name" label:"分类名称" validate:"required"` + ClassIcon string `json:"class_icon" form:"class_icon" label:"图标地址" validate:"required"` + ClassBrief string `json:"class_brief" form:"class_brief" label:"分类简介" validate:"required"` + Sort uint `json:"sort" form:"sort" label:"排序值" validate:"required,number,min=1"` +} + +// AddBaseClass 新增基础分类 +type AddBaseClass struct { + ClassName string `json:"class_name" form:"class_name" label:"分类名称" validate:"required"` + ClassIcon string `json:"class_icon" form:"class_icon" label:"图标地址" validate:"required"` + ClassBrief string `json:"class_brief" form:"class_brief" label:"分类简介" validate:"required"` + Sort uint `json:"sort" form:"sort" label:"排序值" validate:"required,number,min=1"` +} diff --git a/api/requests/Coupon.go b/api/requests/Coupon.go new file mode 100644 index 0000000..dc3d8eb --- /dev/null +++ b/api/requests/Coupon.go @@ -0,0 +1,43 @@ +package requests + +type CouponRequest struct { + GetCouponPage // 获取系统优惠卷列表-分页 + PutCouponStatus // 修改系统优惠卷状态 + AddSystemCoupon // 新增系统优惠卷 +} + +// GetCouponPage 获取系统优惠卷列表-分页 +type GetCouponPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + CouponName string `json:"coupon_name" form:"coupon_name" label:"优惠券名称"` + CouponType *int `json:"coupon_type" form:"coupon_type" label:"优惠券类型"` // (1:无门槛 2:满减) + CouponStatus *int `json:"coupon_status" form:"coupon_status" label:"状态"` // (1:正常 2:强制失效 3:结束 4:删除) + ApplicationScope *int `json:"application_scope" form:"application_scope" label:"适用范围"` // 适用范围(1:全场通用 2:单项 3:会员) + IsMutex *int `json:"is_mutex" form:"is_mutex" label:"是否互斥"` // (0:否 1:是) + ValidType *int `json:"valid_type" form:"valid_type" label:"有效类型"` // (1:绝对时效,xxx-xxx时间段有效 2:相对时效 n天内有效) + CouponDesc string `json:"coupon_desc" form:"coupon_desc" label:"优惠券描述"` +} + +// PutCouponStatus 修改系统优惠卷状态 +type PutCouponStatus struct { + CouponStatus int `json:"coupon_status" form:"coupon_status" label:"状态" validate:"required,oneof=2 3 4"` // (1:正常 2:强制失效 3:结束 4:删除) +} + +// AddSystemCoupon 新增系统优惠卷 +type AddSystemCoupon struct { + CouponName string `json:"coupon_name" form:"coupon_name" label:"优惠券名称" validate:"required"` + CouponType int `json:"coupon_type" form:"coupon_type" label:"优惠券类型" validate:"required,oneof=1 2"` // (1:无门槛 2:满减) + ApplicationScope int `json:"application_scope" form:"application_scope" label:"适用范围" validate:"required,oneof=1 2 3"` // 适用范围(1:全场通用 2:单项 3:会员) + IsMutex int `json:"is_mutex" form:"is_mutex" label:"是否互斥" validate:"required,oneof=0 1"` // (0:否 1:是) + CouponCount int `json:"coupon_count" form:"coupon_count" label:"发放数量" validate:"required,number,min=1"` + CouponPrice float64 `json:"coupon_price" form:"coupon_price" label:"优惠券金额" validate:"required,numeric,gt=0"` + WithAmount *float64 `json:"with_amount" form:"with_amount" label:"满减条件金额" validate:"omitempty,gt=1"` + ValidType int `json:"valid_type" form:"valid_type" label:"有效类型" validate:"required,oneof=1 2"` // (1:绝对时效,xxx-xxx时间段有效 2:相对时效 n天内有效) + ValidDays *int `json:"valid_days" form:"valid_days" label:"有效天数" validate:"omitempty,numeric,min=1"` + ValidStartTime *string `json:"valid_start_time" form:"valid_start_time" label:"开始使用时间"` // 假设转换为字符串格式 + ValidEndTime *string `json:"valid_end_time" form:"valid_end_time" label:"结束使用时间"` // 假设转换为字符串格式 + QuestionId *string `json:"question_id" form:"question_id" label:"问题id"` // 从int64转换为string + SystemMemberId *string `json:"system_member_id" form:"system_member_id" label:"会员id"` // 从int64转换为string + CouponDesc string `json:"coupon_desc" form:"coupon_desc" label:"优惠券描述"` +} diff --git a/api/requests/OrderMember.go b/api/requests/OrderMember.go new file mode 100644 index 0000000..a1e4683 --- /dev/null +++ b/api/requests/OrderMember.go @@ -0,0 +1,28 @@ +package requests + +type OrderMemberRequest struct { + GetOrderMemberPage // 获取会员订单列表-分页 + PutOrderMemberDeleteStatus // 操作会员订单删除状态 +} + +// GetOrderMemberPage 获取会员订单列表-分页 +type GetOrderMemberPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + UserId int64 `json:"user_id" form:"user_id" label:"用户id"` + SystemMemberId string `json:"system_member_id" form:"system_member_id" label:"会员id"` + OrderStatus *int `json:"order_status" form:"order_status" label:"订单状态"` // 订单状态(1:待支付 2:已完成 3:已取消) + PayChannel *int `json:"pay_channel" form:"pay_channel" label:"支付渠道"` // 支付渠道(1:h5支付 2:app支付 3:会员支付) + PayStatus *int `json:"pay_status" form:"pay_status" label:"支付状态"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + RefundStatus *int `json:"refund_status" form:"refund_status" label:"订单退款状态"` + OrderNo string `json:"order_no" form:"order_no" label:"系统订单编号"` + EscrowTradeNo string `json:"escrow_trade_no" form:"escrow_trade_no" label:"第三方支付流水号"` + CancelStatus *int `json:"cancel_status" form:"cancel_status" label:"取消状态"` + MemberDays *int `json:"member_days" form:"member_days" label:"会员天数"` + UserName string `json:"user_name" form:"user_name" label:"用户名称"` +} + +// PutOrderMemberDeleteStatus 操作会员订单删除状态 +type PutOrderMemberDeleteStatus struct { + IsDelete int `json:"is_delete" form:"is_delete" label:"删除状态"` // 用户删除状态(0:否 1:是) +} diff --git a/api/requests/OrderSingle.go b/api/requests/OrderSingle.go new file mode 100644 index 0000000..32e9d67 --- /dev/null +++ b/api/requests/OrderSingle.go @@ -0,0 +1,28 @@ +package requests + +type OrderSingleRequest struct { + GetOrderSinglePage // 获取单项订单列表-分页 + PutOrderSingleDeleteStatus // 操作单项订单删除状态 +} + +// GetOrderSinglePage 获取单项订单列表-分页 +type GetOrderSinglePage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + UserId int64 `json:"user_id" form:"user_id" label:"用户id"` + QuestionId string `json:"question_id" form:"question_id" label:"问题id"` + OrderStatus *int `json:"order_status" form:"order_status" label:"订单状态"` + PayChannel *int `json:"pay_channel" form:"pay_channel" label:"支付渠道"` + PayStatus *int `json:"pay_status" form:"pay_status" label:"支付状态"` + RefundStatus *int `json:"refund_status" form:"refund_status" label:"订单退款状态"` + OrderNo string `json:"order_no" form:"order_no" label:"系统订单编号"` + EscrowTradeNo string `json:"escrow_trade_no" form:"escrow_trade_no" label:"第三方支付流水号"` + CancelStatus *int `json:"cancel_status" form:"cancel_status" label:"取消状态"` + UserName string `json:"user_name" form:"user_name" label:"用户名称"` + QuestionTitle string `json:"question_title" form:"question_title" label:"问题标题"` +} + +// PutOrderSingleDeleteStatus 操作单项订单删除状态 +type PutOrderSingleDeleteStatus struct { + IsDelete int `json:"is_delete" form:"is_delete" label:"删除状态"` // 用户删除状态(0:否 1:是) +} diff --git a/api/requests/Public.go b/api/requests/Public.go new file mode 100644 index 0000000..d46c564 --- /dev/null +++ b/api/requests/Public.go @@ -0,0 +1,21 @@ +package requests + +type PublicRequest struct { + Login // 登陆 + GetIndexData // 首页动态统计数据 +} + +// Login 登陆 +type Login struct { + Access string `json:"access" form:"access" validate:"required" label:"用户名"` // 用户名 + Password string `json:"password" form:"password" validate:"required" label:"密码"` // 密码 + Captcha string `json:"captcha" form:"captcha" validate:"required" label:"验证码"` // 验证码 + CaptchaId string `json:"captchaId" form:"captchaId" validate:"required"` // 验证码ID +} + +// GetIndexData 首页动态统计数据 +type GetIndexData struct { + Type int `json:"type" form:"type" validate:"required,oneof=1 2 3 4" label:"分类"` // 分类(1:新增用户数 2:新增算算数 3:新增单项支付数 4:新增会员购买数) + StartTime string `json:"start_time" form:"start_time" validate:"required" label:"开始时间"` + EndTime string `json:"end_time" form:"end_time" validate:"required" label:"结束时间"` +} diff --git a/api/requests/Question.go b/api/requests/Question.go new file mode 100644 index 0000000..0677831 --- /dev/null +++ b/api/requests/Question.go @@ -0,0 +1,112 @@ +package requests + +type QuestionRequest struct { + GetQuestionPage // 获取问题列表-分页 + GetQuestionList // 获取问题列表 + PutQuestion // 修改问题 + AddQuestion // 新增问题 + PutQuestionStatus // 操作问题发布状态 + PutQuestionHideStatus // 操作问题隐藏状态 +} + +// GetQuestionPage 获取问题列表-分页 +type GetQuestionPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + QuestionId string `json:"question_id" form:"question_id" label:"主键id"` + QuestionTitle string `json:"question_title" form:"question_title" label:"标题"` + QuestionSubtitle string `json:"question_subtitle" form:"question_subtitle" label:"副标题"` + QuestionIden string `json:"question_iden" form:"question_iden" label:"唯一标识"` + QuestionStatus *int `json:"question_status" form:"question_status" label:"问题状态"` // 问题状态(1:正常 2:待发布) + IsHide *int `json:"is_hide" form:"is_hide" label:"是否隐藏"` // 是否隐藏(0:否 1:是) + IsRecommend *int `json:"is_recommend" form:"is_recommend" label:"是否推荐"` // 是否推荐(0:否 1:是) + QuestionBrief string `json:"question_brief" form:"question_brief" label:"问题介绍"` + QuestionExplain string `json:"question_explain" form:"question_explain" label:"问题解释/科普"` + ClassId string `json:"class_id" form:"class_id" label:"分类标识"` + Order *GetQuestionPageOrder `json:"order" form:"order" label:"排序"` +} + +// GetQuestionPageOrder 获取问答题库列表-分页-排序条件 +type GetQuestionPageOrder struct { + ClickCount string `json:"click_count" form:"click_count" label:"排序"` // 点击次数(点击进入详情页的人次) + SubmitCount string `json:"submit_count" form:"submit_count" label:"排序"` // 提交次数(提交个人信息进行了算算的人次) + PayCount string `json:"pay_count" form:"pay_count" label:"排序"` // 支付次数(查看报告的人次) + Price string `json:"price" form:"price" label:"排序"` // 价格(原价) + DiscountPrice string `json:"discount_price" form:"discount_price" label:"排序"` // 优惠价格 + UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"` +} + +// GetQuestionList 获取问题列表 +type GetQuestionList struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + QuestionId string `json:"question_id" form:"question_id" label:"主键id"` + QuestionTitle string `json:"question_title" form:"question_title" label:"标题"` + QuestionSubtitle string `json:"question_subtitle" form:"question_subtitle" label:"副标题"` + QuestionIden string `json:"question_iden" form:"question_iden" label:"唯一标识"` + QuestionStatus *int `json:"question_status" form:"question_status" label:"问题状态"` // 问题状态(1:正常 2:待发布) + IsHide *int `json:"is_hide" form:"is_hide" label:"是否隐藏"` // 是否隐藏(0:否 1:是) + IsRecommend *int `json:"is_recommend" form:"is_recommend" label:"是否推荐"` // 是否推荐(0:否 1:是) + QuestionBrief string `json:"question_brief" form:"question_brief" label:"问题介绍"` + QuestionExplain string `json:"question_explain" form:"question_explain" label:"问题解释/科普"` + ClassId string `json:"class_id" form:"class_id" label:"分类标识"` + Order *GetQuestionPageOrder `json:"order" form:"order" label:"排序"` +} + +// GetQuestionListOrder 获取问题列表-排序条件 +type GetQuestionListOrder struct { + ClickCount string `json:"click_count" form:"click_count" label:"排序"` // 点击次数(点击进入详情页的人次) + SubmitCount string `json:"submit_count" form:"submit_count" label:"排序"` // 提交次数(提交个人信息进行了算算的人次) + PayCount string `json:"pay_count" form:"pay_count" label:"排序"` // 支付次数(查看报告的人次) + Price string `json:"price" form:"price" label:"排序"` // 价格(原价) + DiscountPrice string `json:"discount_price" form:"discount_price" label:"排序"` // 优惠价格 + UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"` +} + +// PutQuestion 修改问题 +type PutQuestion struct { + QuestionTitle string `json:"question_title" form:"question_title" label:"标题" validate:"required"` + QuestionSubtitle string `json:"question_subtitle" form:"question_subtitle" label:"副标题" validate:"required"` + QuestionIden string `json:"question_iden" form:"question_iden" label:"唯一标识" validate:"required"` + QuestionStatus int `json:"question_status" form:"question_status" label:"问题状态" validate:"required,oneof=1 2"` // 问题状态(1:正常 2:待发布) + IsHide *int `json:"is_hide" form:"is_hide" label:"是否隐藏" validate:"required,oneof=0 1"` // 是否隐藏(0:否 1:是) + IsRecommend *int `json:"is_recommend" form:"is_recommend" label:"是否推荐" validate:"required,oneof=0 1"` // 是否推荐(0:否 1:是) + ClickCount *int `json:"click_count" form:"click_count" label:"点击次数" validate:"required,numeric,min=0"` // 点击次数(点击进入详情页的人次) + SubmitCount *int `json:"submit_count" form:"submit_count" label:"提交次数" validate:"required,numeric,min=0"` // 提交次数(提交个人信息进行了算算的人次) + PayCount *int `json:"pay_count" form:"pay_count" label:"支付次数" validate:"required,numeric,min=0"` // 支付次数(查看报告的人次) + Price *float64 `json:"price" form:"price" label:"价格" validate:"required,numeric,min=0"` // (原价) + DiscountPrice *float64 `json:"discount_price" form:"discount_price" label:"优惠价格" validate:"omitempty,numeric,min=0"` // 优惠价格 + DiscountEndTime *string `json:"discount_end_time" form:"discount_end_time" label:"优惠截止时间"` // 优惠截止时间 + QuestionBrief string `json:"question_brief" form:"question_brief" label:"问题介绍" validate:"required"` + QuestionExplain string `json:"question_explain" form:"question_explain" label:"问题解释/科普" validate:"required"` + ClassId []*string `json:"class_id" form:"class_id" label:"分类" validate:"required"` // 分类标识 +} + +// AddQuestion 新增问题 +type AddQuestion struct { + QuestionTitle string `json:"question_title" form:"question_title" label:"标题" validate:"required"` + QuestionSubtitle string `json:"question_subtitle" form:"question_subtitle" label:"副标题" validate:"required"` + QuestionIden string `json:"question_iden" form:"question_iden" label:"唯一标识" validate:"required"` + QuestionStatus int `json:"question_status" form:"question_status" label:"问题状态" validate:"required,oneof=1 2"` // 问题状态(1:正常 2:待发布) + IsHide *int `json:"is_hide" form:"is_hide" label:"是否隐藏" validate:"required,oneof=0 1"` // 是否隐藏(0:否 1:是) + IsRecommend *int `json:"is_recommend" form:"is_recommend" label:"是否推荐" validate:"required,oneof=0 1"` // 是否推荐(0:否 1:是) + ClickCount *int `json:"click_count" form:"click_count" label:"点击次数" validate:"required,numeric,min=0"` // 点击次数(点击进入详情页的人次) + SubmitCount *int `json:"submit_count" form:"submit_count" label:"提交次数" validate:"required,numeric,min=0"` // 提交次数(提交个人信息进行了算算的人次) + PayCount *int `json:"pay_count" form:"pay_count" label:"支付次数" validate:"required,numeric,min=0"` // 支付次数(查看报告的人次) + Price *float64 `json:"price" form:"price" label:"价格" validate:"required,numeric,min=0"` // (原价) + DiscountPrice *float64 `json:"discount_price" form:"discount_price" label:"优惠价格" validate:"omitempty,numeric,min=0"` // 优惠价格 + DiscountEndTime *string `json:"discount_end_time" form:"discount_end_time" label:"优惠截止时间"` // 优惠截止时间 + QuestionBrief string `json:"question_brief" form:"question_brief" label:"问题介绍" validate:"required"` + QuestionExplain string `json:"question_explain" form:"question_explain" label:"问题解释/科普" validate:"required"` + ClassId []*string `json:"class_id" form:"class_id" label:"分类" validate:"required"` // 分类标识 +} + +// PutQuestionStatus 操作问题发布状态 +type PutQuestionStatus struct { + QuestionStatus int `json:"question_status" form:"question_status" label:"状态" validate:"required,oneof=1 2"` // 问题状态(1:正常 2:待发布) +} + +// PutQuestionHideStatus 操作问题隐藏状态 +type PutQuestionHideStatus struct { + IsHide int `json:"is_hide" form:"is_hide" label:"状态" validate:"required,oneof=0 1"` // 是否隐藏(0:否 1:是) +} diff --git a/api/requests/SystemMember.go b/api/requests/SystemMember.go new file mode 100644 index 0000000..a4ec719 --- /dev/null +++ b/api/requests/SystemMember.go @@ -0,0 +1,22 @@ +package requests + +type SystemMemberRequest struct { + PutSystemMember // 修改会员配置 + AddSystemMember // 新增会员配置 +} + +// PutSystemMember 修改会员配置 +type PutSystemMember struct { + MemberDays uint `json:"member_days" form:"member_days" label:"会员天数" validate:"required,numeric,min=0"` + Price float64 `json:"price" form:"price" label:"价格(原价)" validate:"required,numeric,min=0"` + DiscountPrice *float64 `json:"discount_price" form:"discount_price" label:"优惠价格" validate:"omitempty,numeric,min=0"` + DiscountEndTime *string `json:"discount_end_time" form:"discount_end_time" label:"优惠截止时间"` +} + +// AddSystemMember 新增会员配置 +type AddSystemMember struct { + MemberDays uint `json:"member_days" form:"member_days" label:"会员天数" validate:"required,numeric,min=0"` + Price float64 `json:"price" form:"price" label:"价格(原价)" validate:"required,numeric,min=0"` + DiscountPrice *float64 `json:"discount_price" form:"discount_price" label:"优惠价格" validate:"omitempty,numeric,min=0"` + DiscountEndTime *string `json:"discount_end_time" form:"discount_end_time" label:"优惠截止时间"` +} diff --git a/api/requests/SystemSingle.go b/api/requests/SystemSingle.go new file mode 100644 index 0000000..7da3921 --- /dev/null +++ b/api/requests/SystemSingle.go @@ -0,0 +1,18 @@ +package requests + +type SystemSingleRequest struct { + PutSystemSingle // 修改单项配置 + AddSystemSingle // 新增单项配置 +} + +// PutSystemSingle 修改单项配置 +type PutSystemSingle struct { + FirstTimePrice float64 `json:"first_time_price" form:"first_time_price" label:"首次购买价格" validate:"required,numeric,min=0"` + ValidDays int `json:"valid_days" form:"valid_days" label:"购买后有效天数" validate:"required,numeric,min=0"` +} + +// AddSystemSingle 新增单项配置 +type AddSystemSingle struct { + FirstTimePrice float64 `json:"first_time_price" form:"first_time_price" label:"首次购买价格" validate:"required,numeric,min=0"` + ValidDays int `json:"valid_days" form:"valid_days" label:"购买后有效天数" validate:"required,numeric,min=0"` +} diff --git a/api/requests/User.go b/api/requests/User.go new file mode 100644 index 0000000..48adf43 --- /dev/null +++ b/api/requests/User.go @@ -0,0 +1,36 @@ +package requests + +type UserRequest struct { + GetUserPage // 获取用户列表-分页 + PutUserStatus // 操作用户状态 +} + +// GetUserPage 获取用户列表-分页 +type GetUserPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + UserId string `json:"user_id" form:"user_id" label:"用户id"` + UserName string `json:"user_name" form:"user_name" label:"用户名称"` + Mobile string `json:"mobile" form:"mobile" label:"手机号"` + UserStatus int `json:"user_status" form:"user_status" label:"状态"` + RegisterSource int `json:"register_source" form:"register_source" label:"注册来源"` + OpenId string `json:"open_id" form:"open_id" label:"用户微信标识"` + Sex *int `json:"sex" form:"sex" label:"性别"` + IsMember *int `json:"is_member" form:"is_member" label:"是否会员"` + MemberExpireDate string `json:"member_expire_date" form:"member_expire_date" label:"会员到期时间"` // 假设转换为可选字符串 + IsInfoComplete *int `json:"is_info_complete" form:"is_info_complete" label:"信息完善状态"` // 0:否 1:是 + Order *GetUserPageOrder `json:"order" form:"order" label:"排序"` +} + +// GetUserPageOrder 获取用户列表-分页-排序条件 +type GetUserPageOrder struct { + UpdatedAt string `json:"updated_at" form:"updated_at" label:"排序"` + SingleSubmitCount string `json:"single_submit_count" form:"single_submit_count" label:"排序"` // 提交次数 + SinglePayCount string `json:"single_pay_count" form:"single_pay_count" label:"排序"` // 支付次数 + MemberBuyCount string `json:"member_buy_count" form:"member_buy_count" label:"排序"` // 会员购买次数 +} + +// PutUserStatus 操作用户状态 +type PutUserStatus struct { + UserStatus int `json:"user_status" form:"user_status" label:"删除状态" validate:"required,oneof=1 2"` // 状态(1:正常 2:禁用) +} diff --git a/api/requests/UserCoupon.go b/api/requests/UserCoupon.go new file mode 100644 index 0000000..05850bd --- /dev/null +++ b/api/requests/UserCoupon.go @@ -0,0 +1,15 @@ +package requests + +type UserCouponRequest struct { + GetUserCouponPage // 获取用户优惠卷列表-分页 +} + +// GetUserCouponPage 获取用户优惠卷列表-分页 +type GetUserCouponPage struct { + Page int `json:"page" form:"page" label:"页码"` + PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + UserId *int64 `json:"user_id" form:"user_id" label:"用户id"` + CouponId string `json:"coupon_id" form:"coupon_id" label:"优惠券id"` + UserCouponStatus *int `json:"user_coupon_status" form:"user_coupon_status" label:"状态"` // 状态(0:未使用 1:已使用 3:已过期) + UserName string `json:"user_name" form:"user_name" label:"用户名称"` +} diff --git a/api/requests/base.go b/api/requests/base.go new file mode 100644 index 0000000..309b903 --- /dev/null +++ b/api/requests/base.go @@ -0,0 +1,4 @@ +package requests + +type Requests struct { +} diff --git a/api/responses/responses.go b/api/responses/responses.go new file mode 100644 index 0000000..4e7545d --- /dev/null +++ b/api/responses/responses.go @@ -0,0 +1,52 @@ +package responses + +import ( + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/consts" + "net/http" +) + +type res struct { + Code int `json:"code"` + Data interface{} `json:"data"` + Message string `json:"message"` +} + +func result(code int, data interface{}, msg string, c *gin.Context) { + if data == nil { + data = gin.H{} + } + c.JSON(http.StatusOK, res{ + code, + data, + msg, + }) +} + +func Ok(c *gin.Context) { + result(consts.HttpSuccess, map[string]interface{}{}, "成功", c) +} + +func OkWithMessage(message string, c *gin.Context) { + result(consts.HttpSuccess, map[string]interface{}{}, message, c) +} + +func OkWithData(data interface{}, c *gin.Context) { + result(consts.HttpSuccess, data, "成功", c) +} + +func OkWithDetailed(data interface{}, message string, c *gin.Context) { + result(consts.HttpSuccess, data, message, c) +} + +func Fail(c *gin.Context) { + result(consts.HttpError, map[string]interface{}{}, "失败", c) +} + +func FailWithMessage(message string, c *gin.Context) { + result(consts.HttpError, map[string]interface{}{}, message, c) +} + +func FailWithDetailed(data interface{}, message string, c *gin.Context) { + result(consts.HttpError, data, message, c) +} diff --git a/api/router/router.go b/api/router/router.go new file mode 100644 index 0000000..955ccb1 --- /dev/null +++ b/api/router/router.go @@ -0,0 +1,290 @@ +package router + +import ( + "fmt" + "github.com/gin-gonic/gin" + "hepa-calc-admin-api/api/controller" + "hepa-calc-admin-api/api/exception" + "hepa-calc-admin-api/api/middlewares" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/consts" + "net/http" +) + +// Init 初始化路由 +func Init() *gin.Engine { + r := gin.New() + + // 环境设置 + if config.C.Env == "prod" { + gin.SetMode(gin.ReleaseMode) + } + + // 获取请求参数中间件-json格式下会导致接口获取不到请求数据 + r.Use(middlewares.RequestParamsMiddleware()) + + // 日志中间件 + r.Use(middlewares.Logrus()) + + // 异常 + r.Use(gin.Recovery()) + + // 404处理 + r.NoRoute(func(c *gin.Context) { + path := c.Request.URL.Path + method := c.Request.Method + c.JSON(http.StatusNotFound, gin.H{ + "msg": fmt.Sprintf("%s %s not found", method, path), + "code": consts.ClientHttpNotFound, + "data": "", + }) + }) + + // 异常处理 + r.Use(exception.Recover()) + + // 跨域处理 + r.Use(middlewares.Cors()) + + // 加载基础路由 + api := controller.Api{} + + // 公开路由-不验证权限 + publicRouter(r, api) + + // 验证jwt + r.Use(middlewares.Jwt()) + + // 验证权限 + r.Use(middlewares.Auth()) + + // 私有路由-验证权限 + privateRouter(r, api) + + // 公共路由-验证权限 + adminRouter(r, api) + + // 基础数据-验证权限 + basicRouter(r, api) + + return r +} + +// publicRouter 公开路由-不验证权限 +func publicRouter(r *gin.Engine, api controller.Api) { + adminGroup := r.Group("/admin") + + // 登录 + adminGroup.POST("/login", api.Public.Login) + + // 验证码 + adminGroup.GET("/captcha", api.Public.GetCaptcha) +} + +// adminRouter 公共路由-验证权限 +func adminRouter(r *gin.Engine, api controller.Api) { + adminGroup := r.Group("/admin") + + // 首页 + indexGroup := adminGroup.Group("/index") + { + // 首页 + indexGroup.GET("", api.Public.GetIndex) + + // 首页动态统计数据 + indexGroup.GET("/data", api.Public.GetIndexData) + } +} + +// basicRouter 基础数据-验证权限 +func basicRouter(r *gin.Engine, api controller.Api) { + +} + +// privateRouter 私有路由-验证权限 +func privateRouter(r *gin.Engine, api controller.Api) { + adminGroup := r.Group("/admin") + + // 订单 + orderGroup := adminGroup.Group("/order") + { + // 单项订单 + singleGroup := orderGroup.Group("/single") + { + // 获取单项订单列表-分页 + singleGroup.GET("/page", api.OrderSingle.GetOrderSinglePage) + + // 获取单项订单详情 + singleGroup.GET("/:order_id", api.OrderSingle.GetOrderSingle) + + // 取消单项订单 + singleGroup.PUT("/cancel/:order_id", api.OrderSingle.PutCancelOrderSingle) + + // 操作单项订单删除状态 + singleGroup.PUT("/delete/:order_id", api.OrderSingle.PutOrderSingleDeleteStatus) + + // 获取单项订单操作结果 + } + + // 会员订单 + memberGroup := orderGroup.Group("/member") + { + // 获取会员订单列表-分页 + memberGroup.GET("/page", api.OrderMember.GetOrderMemberPage) + + // 获取会员订单详情 + memberGroup.GET("/:order_id", api.OrderMember.GetOrderMember) + + // 取消会员订单 + memberGroup.PUT("/cancel/:order_id", api.OrderMember.PutCancelOrderMember) + + // 操作会员订单删除状态 + memberGroup.PUT("/delete/:order_id", api.OrderMember.PutOrderMemberDeleteStatus) + } + } + + // 用户 + userGroup := adminGroup.Group("/user") + { + // 获取用户列表-分页 + userGroup.POST("/page", api.User.GetUserPage) + + // 获取用户详情 + userGroup.GET("/:user_id", api.User.GetUser) + + // 操作用户状态 + userGroup.PUT("/status/:user_id", api.User.PutUserStatus) + + // 获取用户信息详情 + + } + + // 优惠卷 + couponGroup := adminGroup.Group("/coupon") + { + // 系统优惠卷 + systemGroup := couponGroup.Group("/system") + { + // 获取系统优惠卷列表-分页 + systemGroup.GET("/page", api.Coupon.GetCouponPage) + + // 获取系统优惠卷详情 + systemGroup.GET("/:coupon_id", api.Coupon.GetCoupon) + + // 添加系统优惠卷 + systemGroup.POST("", api.Coupon.AddSystemCoupon) + + // 操作系统优惠卷状态 + systemGroup.PUT("/status/:coupon_id", api.Coupon.PutCouponStatus) + } + + // 用户优惠卷 + userGroup := couponGroup.Group("/user") + { + // 获取用户优惠卷列表-分页 + userGroup.GET("/page", api.UserCoupon.GetUserCouponPage) + + // 删除用户优惠卷 + userGroup.DELETE("/:user_coupon_id", api.UserCoupon.DeleteUserCoupon) + } + } + + // 分类 + classGroup := adminGroup.Group("/class") + { + // 获取基础分类列表-分页 + classGroup.POST("/page", api.BaseClass.GetBaseClassPage) + + // 获取基础分类列表 + classGroup.POST("/list", api.BaseClass.GetBaseClassList) + + // 操作基础分类状态 + classGroup.PUT("/status/:class_id", api.BaseClass.PutBaseClassStatus) + + // 获取基础分类详情 + classGroup.GET("/:class_id", api.BaseClass.GetBaseClass) + + // 修改基础分类 + classGroup.PUT("/:class_id", api.BaseClass.PutBaseClass) + + // 新增基础分类 + classGroup.POST("", api.BaseClass.AddBaseClass) + } + + // 问题 + questionGroup := adminGroup.Group("/question") + { + // 获取问题列表-分页 + questionGroup.POST("/page", api.Question.GetQuestionPage) + + // 获取问题列表 + questionGroup.POST("/list", api.Question.GetQuestionList) + + // 获取问题详情 + questionGroup.GET("/:question_id", api.Question.GetQuestion) + + // 修改问题 + questionGroup.PUT("/:question_id", api.Question.PutQuestion) + + // 新增问题 + questionGroup.POST("", api.Question.AddQuestion) + + // 操作问题发布状态 + questionGroup.PUT("/status/:question_id", api.Question.PutQuestionStatus) + + // 操作问题隐藏状态 + questionGroup.PUT("/hide/:question_id", api.Question.PutQuestionHideStatus) + } + + // 配置 + systemGroup := adminGroup.Group("/system") + { + // 会员配置 + memberGroup := systemGroup.Group("/member") + { + // 获取会员配置列表 + memberGroup.GET("/list", api.SystemMember.GetSystemMemberList) + + // 获取会员配置详情 + memberGroup.GET("/:system_member_id", api.SystemMember.GetSystemMember) + + // 修改会员配置 + memberGroup.PUT("/:system_member_id", api.SystemMember.PutSystemMember) + + // 新增会员配置 + memberGroup.POST("", api.SystemMember.AddSystemMember) + } + + // 单项配置 + singleGroup := systemGroup.Group("/single") + { + // 获取单项配置列表 + singleGroup.GET("/list", api.SystemSingle.GetSystemSingleList) + + // 获取单项配置详情 + singleGroup.GET("/:system_single_id", api.SystemSingle.GetSystemSingle) + + // 修改单项配置 + singleGroup.PUT("/:system_single_id", api.SystemSingle.PutSystemSingle) + + // 新增单项配置 + singleGroup.POST("", api.SystemSingle.AddSystemSingle) + } + } + + // 协议 + agreementGroup := adminGroup.Group("/agreement") + { + // 获取协议列表-分页 + agreementGroup.GET("/page", api.BaseAgreement.GetBaseAgreementPage) + + // 获取协议详情 + agreementGroup.GET("/:agreement_id", api.BaseAgreement.GetBaseAgreement) + + // 修改协议 + agreementGroup.PUT("/:agreement_id", api.BaseAgreement.PutBaseAgreement) + + // 新增协议 + agreementGroup.POST("", api.BaseAgreement.AddBaseAgreement) + } +} diff --git a/api/service/BaseClass.go b/api/service/BaseClass.go new file mode 100644 index 0000000..d7ed29a --- /dev/null +++ b/api/service/BaseClass.go @@ -0,0 +1,4 @@ +package service + +type BaseClassService struct { +} diff --git a/api/service/OrderMember.go b/api/service/OrderMember.go new file mode 100644 index 0000000..09ddd5c --- /dev/null +++ b/api/service/OrderMember.go @@ -0,0 +1,185 @@ +package service + +import ( + "context" + "errors" + "fmt" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" + "gorm.io/gorm" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/extend/weChat" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "time" +) + +type OrderMemberService struct { +} + +// CancelOrderMember 取消会员订单 +// cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) +func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { + // 检测多次请求 + redisKey := "CancelOrderMember" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) + res, _ := global.Redis.Get(context.Background(), redisKey).Result() + if res != "" { + return false, errors.New("请勿重复操作") + } + + defer func(redisKey string) { + global.Redis.Del(context.Background(), redisKey) + }(redisKey) + + // 添加缓存 + _, err := global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() + if err != nil { + return false, errors.New("取消订单失败") + } + + // 获取订单数据 + orderMemberDao := dao.OrderMemberDao{} + maps := make(map[string]interface{}) + maps["order_id"] = orderId + orderMember, err := orderMemberDao.GetOrderMember(maps) + if err != nil { + return false, errors.New("订单异常") + } + + // 订单状态(1:待支付 2:已完成 3:已取消) + if orderMember.OrderStatus == 2 { + return false, errors.New("订单已完成,无法取消") + } + + if orderMember.OrderStatus == 3 { + return false, errors.New("订单已取消,请勿重复操作") + } + + // 取消状态(0:否 1:是) + if orderMember.CancelStatus == 1 { + return false, errors.New("订单已取消") + } + + // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + if orderMember.PayStatus == 2 { + return false, errors.New("订单已支付,无法取消") + } + + // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + if orderMember.RefundStatus == 1 { + return false, errors.New("订单已申请退款") + } + + if orderMember.RefundStatus == 2 { + return false, errors.New("订单退款中") + } + + if orderMember.RefundStatus == 3 { + return false, errors.New("订单已退款成功") + } + + if orderMember.RefundStatus == 6 { + return false, errors.New("订单退款异常") + } + + // 修改订单为取消 + orderMemberData := make(map[string]interface{}) + orderMemberData["order_status"] = 3 + if cancelReason == 3 { + // 支付超时取消 + orderMemberData["pay_status"] = 5 + } + orderMemberData["cancel_status"] = 1 + orderMemberData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") + orderMemberData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) + orderMemberData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderMemberDao.EditOrderMemberById(tx, orderId, orderMemberData) + if err != nil { + return false, errors.New("订单取消失败") + } + + // 退还订单优惠卷 + if orderMember.CouponAmountTotal != 0 { + // 获取订单优惠卷数据 + orderMemberCouponDao := dao.OrderMemberCouponDao{} + orderMemberCoupon, err := orderMemberCouponDao.GetOrderMemberCouponByOrderId(orderId) + if err != nil { + tx.Rollback() + return false, errors.New("订单取消失败") + } + + userCouponService := &UserCouponService{} + userCouponService.ReturnUserCoupon(tx, orderMemberCoupon.UserCouponId) + } + + return true, nil +} + +// GetJsapiPrepay 获取jsapi预支付交易会话标识 +func (r *OrderMemberService) GetJsapiPrepay(m *model.OrderMember) (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId != "" { + return nil, errors.New("发起支付失败") + } + + jsapiRequest := weChat.JsapiRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1281030301.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.JsapiRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + Payer: weChat.JsapiRequestPayerRequest{OpenId: user.OpenId}, + } + + prepay, err = jsapiRequest.GetJsapiPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} + +// GetAppPrepay 获取app预支付交易会话标识 +func (r *OrderMemberService) GetAppPrepay(m *model.OrderMember) (prepay *app.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId != "" { + return nil, errors.New("发起支付失败") + } + + appRequest := weChat.AppRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1281030301.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.AppRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + } + + prepay, err = appRequest.GetAppPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} diff --git a/api/service/OrderSingle.go b/api/service/OrderSingle.go new file mode 100644 index 0000000..1f5e108 --- /dev/null +++ b/api/service/OrderSingle.go @@ -0,0 +1,410 @@ +package service + +import ( + "context" + "errors" + "fmt" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" + "gorm.io/gorm" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/extend/rabbitMq" + "hepa-calc-admin-api/extend/weChat" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "time" +) + +type OrderSingleService struct { +} + +// AddOrderSingle 创建单项订单 +// payChannel:支付渠道(1:h5支付 2:app支付 3:会员支付) +func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int64, UserCouponId *int64, payChannel int, orderPrice float64) (orderSingle *model.OrderSingle, err error) { + // 检测并发请求 + redisKey := "AddOrderSingle" + fmt.Sprintf("%d", UserId) + fmt.Sprintf("%d", QuestionId) + res, _ := global.Redis.Get(context.Background(), redisKey).Result() + if res != "" { + return nil, errors.New("请勿重复操作") + } + + defer func(redisKey string) { + global.Redis.Del(context.Background(), redisKey) + }(redisKey) + + // 添加缓存 + _, err = global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() + if err != nil { + return nil, errors.New("生成订单失败") + } + + // 获取题目数据 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(QuestionId) + if err != nil { + return nil, errors.New("题目异常") + } + + // 检测题目 + questionService := &QuestionService{} + isNormal, err := questionService.CheckQuestion(question) + if err != nil || isNormal == false { + return nil, err + } + + var amountTotal *float64 // 总金额 + var couponAmountTotal float64 // 优惠卷总金额 + var paymentAmountTotal float64 // 实际付款金额 + var orderStatus int // 订单状态(1:待支付 2:已完成 3:已取消) + var payStatus int // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + var payTime *time.Time // 支付时间 + var escrowTradeNo string // 第三方支付流水号 + + // 获取问题最终价格 + amountTotal, err = questionService.GetUserBuyPrice(UserId, QuestionId) + if err != nil { + return nil, err + } + + if amountTotal == nil { + return nil, errors.New("价格错误") + } + + // 检测用户优惠卷 + var userCoupon *model.UserCoupon + if UserCouponId != nil { + // 获取优惠卷数据 + UserCouponDao := dao.UserCouponDao{} + userCoupon, err = UserCouponDao.GetUserCouponPreloadById(*UserCouponId) + if err != nil { + return nil, errors.New("优惠券异常") + } + + userCouponService := &UserCouponService{} + isCanUse, err := userCouponService.CheckUserCoupon(userCoupon, QuestionId, 1, *amountTotal) + if err != nil || isCanUse == false { + return nil, errors.New("价格异常") + } + + // 优惠卷总金额 + couponAmountTotal = userCoupon.Coupon.CouponPrice + } + + // 会员支付 + if payChannel == 3 { + paymentAmountTotal = 0 // 实际付款金额 + orderStatus = 2 // 订单状态(1:待支付 2:已完成 3:已取消) + payStatus = 2 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + + now := time.Now() + payTime = &now // 支付时间 + + escrowTradeNo = "GD" + global.Snowflake.Generate().String() // 第三方支付流水号 + } else { + // 实际付款金额 + paymentAmountTotal = *amountTotal - couponAmountTotal + if orderPrice != paymentAmountTotal { + return nil, errors.New("价格异常") + } + + orderStatus = 1 // 订单状态(1:待支付 2:已完成 3:已取消) + payStatus = 1 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + payTime = nil // 支付时间 + } + + // 生成订单号 + orderNo := global.Snowflake.Generate().String() + + // 创建订单 + orderSingle = &model.OrderSingle{ + UserId: UserId, + QuestionId: QuestionId, + OrderStatus: orderStatus, + IsDelete: 0, + PayChannel: payChannel, + PayStatus: payStatus, + PayTime: payTime, + RefundStatus: 0, + OrderNo: orderNo, + EscrowTradeNo: escrowTradeNo, + AmountTotal: *amountTotal, + CouponAmountTotal: couponAmountTotal, + PaymentAmountTotal: paymentAmountTotal, + CancelStatus: 0, + CancelTime: nil, + CancelRemarks: "", + OrderRemarks: "", + } + + orderSingleDao := dao.OrderSingleDao{} + orderSingle, err = orderSingleDao.AddOrderSingle(tx, orderSingle) + if err != nil { + return nil, errors.New("订单创建失败") + } + + // 创建优惠卷表 + if userCoupon != nil { + orderSingleCoupon := &model.OrderSingleCoupon{ + OrderId: orderSingle.OrderId, + UserCouponId: *UserCouponId, + CouponName: userCoupon.Coupon.CouponName, + CouponUsePrice: userCoupon.Coupon.CouponPrice, + } + + orderSingleCouponDao := dao.OrderSingleCouponDao{} + orderSingleCoupon, err = orderSingleCouponDao.AddOrderSingleCoupon(tx, orderSingleCoupon) + if err != nil { + tx.Rollback() + return nil, errors.New("订单创建失败") + } + + // 修改优惠卷使用状态 + userCouponDao := dao.UserCouponDao{} + + userCouponData := make(map[string]interface{}) + userCouponData["user_coupon_status"] = 1 + userCouponData["coupon_use_date"] = time.Now().Format("2006-01-02 15:04:05") + err := userCouponDao.EditUserCouponById(tx, userCoupon.UserCouponId, userCouponData) + if err != nil { + return nil, errors.New("订单创建失败") + } + } + + // 增加未支付取消订单延迟队列 + if payChannel == 1 || payChannel == 2 { + delay := 30 * time.Minute + + if config.C.Env == "dev" { + delay = 3 * time.Minute + } + + data := make(map[string]interface{}) + data["order_id"] = fmt.Sprintf("%d", orderSingle.OrderId) + data["order_no"] = orderSingle.OrderNo + data["user_id"] = fmt.Sprintf("%d", orderSingle.UserId) + data["order_type"] = 1 + data["pay_channel"] = orderSingle.PayChannel + + p := rabbitMq.PublishS{ + QueueName: "cancel.unpay.order.delay.queue", + ExchangeName: "amqp.delay.direct", + RoutingKey: "CancelUnPayOrder", + Message: data, + Delay: delay, + } + err := p.PublishWithDelay() + if err != nil { + utils.LogJsonErr("添加处理取消未支付订单队列失败:", err.Error()) + return nil, errors.New("订单创建失败") + } + } + + return orderSingle, nil +} + +// CancelOrderSingle 取消单项订单 +// cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) +func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { + // 检测多次请求 + redisKey := "CancelOrderSingle" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) + res, _ := global.Redis.Get(context.Background(), redisKey).Result() + if res != "" { + return false, errors.New("请勿重复操作") + } + + defer func(redisKey string) { + global.Redis.Del(context.Background(), redisKey) + }(redisKey) + + // 添加缓存 + _, err := global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() + if err != nil { + return false, errors.New("取消订单失败") + } + + // 获取订单数据 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["order_id"] = orderId + orderSingle, err := orderSingleDao.GetOrderSingle(maps) + if err != nil { + return false, errors.New("订单异常") + } + + // 订单状态(1:待支付 2:已完成 3:已取消) + if orderSingle.OrderStatus == 2 { + return false, errors.New("订单已完成,无法取消") + } + + if orderSingle.OrderStatus == 3 { + return false, errors.New("订单已取消,请勿重复操作") + } + + // 取消状态(0:否 1:是) + if orderSingle.CancelStatus == 1 { + return false, errors.New("订单已取消") + } + + // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + if orderSingle.PayStatus == 2 { + return false, errors.New("订单已支付,无法取消") + } + + // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + if orderSingle.RefundStatus == 1 { + return false, errors.New("订单已申请退款") + } + + if orderSingle.RefundStatus == 2 { + return false, errors.New("订单退款中") + } + + if orderSingle.RefundStatus == 3 { + return false, errors.New("订单已退款成功") + } + + if orderSingle.RefundStatus == 6 { + return false, errors.New("订单退款异常") + } + + // 修改订单为取消 + orderSingleData := make(map[string]interface{}) + orderSingleData["order_status"] = 3 + if cancelReason == 3 { + // 支付超时取消 + orderSingleData["pay_status"] = 5 + } + orderSingleData["cancel_status"] = 1 + orderSingleData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") + orderSingleData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderSingleDao.EditOrderSingleById(tx, orderId, orderSingleData) + if err != nil { + return false, errors.New("订单取消失败") + } + + // 退还订单优惠卷 + if orderSingle.CouponAmountTotal != 0 { + // 获取订单优惠卷数据 + orderSingleCouponDao := dao.OrderSingleCouponDao{} + orderSingleCoupon, err := orderSingleCouponDao.GetOrderSingleCouponByOrderId(orderId) + if err != nil { + tx.Rollback() + return false, errors.New("订单取消失败") + } + + userCouponService := &UserCouponService{} + userCouponService.ReturnUserCoupon(tx, orderSingleCoupon.UserCouponId) + } + + return true, nil +} + +// GetJsapiPrepay 获取jsapi预支付交易会话标识 +func (r *OrderSingleService) GetJsapiPrepay(m *model.OrderSingle) (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId == "" { + return nil, errors.New("发起支付失败") + } + + jsapiRequest := weChat.JsapiRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1281030301.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.JsapiRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + Payer: weChat.JsapiRequestPayerRequest{OpenId: user.OpenId}, + } + + prepay, err = jsapiRequest.GetJsapiPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} + +// GetAppPrepay 获取app预支付交易会话标识 +func (r *OrderSingleService) GetAppPrepay(m *model.OrderSingle) (prepay *app.PrepayWithRequestPaymentResponse, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(m.UserId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + if user.OpenId == "" { + return nil, errors.New("发起支付失败") + } + + appRequest := weChat.AppRequest{ + AppId: config.C.Wechat.AppId, + MchId: config.C.Wechat.Pay1281030301.MchId, + Description: "肝病算一算", + OutTradeNo: m.OrderNo, + NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + Amount: weChat.AppRequestAmountRequest{ + Total: int64(m.PaymentAmountTotal * 100), + Currency: "CNY", + }, + } + + prepay, err = appRequest.GetAppPrepay() + if err != nil { + return nil, err + } + + return prepay, nil +} + +// CompleteUnPayOrderSingle 完成未支付单项订单-开通会员成功时使用 +func (r *OrderSingleService) CompleteUnPayOrderSingle(tx *gorm.DB, userId int64) (bool, error) { + // 获取所有未支付单项订单 + orderSingleDao := dao.OrderSingleDao{} + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["order_status"] = 1 + maps["pay_status"] = 1 + maps["cancel_status"] = 0 + orderSingles, err := orderSingleDao.GetOrderSingleList(maps) + if err != nil { + return false, err + } + + for _, single := range orderSingles { + // 生成第三方支付流水号 + escrowTradeNo := "GD" + global.Snowflake.Generate().String() + + orderSingleData := make(map[string]interface{}) + orderSingleData["order_status"] = 2 + orderSingleData["pay_status"] = 2 + orderSingleData["pay_time"] = time.Now().Format("2006-01-02 15:04:05") + orderSingleData["escrow_trade_no"] = escrowTradeNo + orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") + err = orderSingleDao.EditOrderSingleById(tx, single.OrderId, orderSingleData) + if err != nil { + return false, err + } + + // 增加题目支付次数 + questionDao := dao.QuestionDao{} + err = questionDao.Inc(tx, single.QuestionId, "pay_count", 1) + if err != nil { + return false, err + } + } + + return true, nil +} diff --git a/api/service/Public.go b/api/service/Public.go new file mode 100644 index 0000000..03f0fc9 --- /dev/null +++ b/api/service/Public.go @@ -0,0 +1,85 @@ +package service + +import ( + "context" + "errors" + "hepa-calc-admin-api/extend/aliyun" + "hepa-calc-admin-api/global" + "math/rand" + "net/http" + "strconv" + "time" +) + +type PublicService struct { +} + +// GetPhoneCode 获取手机验证码 +func (r *PublicService) GetPhoneCode(scene int, phone string) (bool, error) { + var sendCodeCount int // // 获取验证码最大次数 + var code string // 验证码 + var templateCode string // 短信模版 + + // 登录获取验证码 + if scene == 1 { + // 验证发送次数 + res, _ := global.Redis.Get(context.Background(), "login_code_count_"+phone).Result() + if res != "" { + sendCodeCount, err := strconv.Atoi(res) + if err != nil { + return false, err + } + + if sendCodeCount > 3 { + // 超出规定时间内最大获取次数 + return false, errors.New("手机号超出规定时间内最大获取次数,请您稍后再试") + } + } + + // 生成随机数 + rand.New(rand.NewSource(time.Now().UnixNano())) + code = strconv.Itoa(rand.Intn(9000) + 1000) + + // 模版 + templateCode = "SMS_243055263" + + sendCodeCount = sendCodeCount + 1 + } + + if code == "" || templateCode == "" { + return false, errors.New("验证码发送失败,请您稍后再试") + } + + // 发送验证码 + templateParam := make(map[string]interface{}) + templateParam["code"] = code + err := aliyun.SendSms(phone, templateCode, "获取验证码", templateParam) + if err != nil { + return false, err + } + + // 记录发送次数 + if sendCodeCount != 0 { + _, err = global.Redis.Set(context.Background(), "login_code_count_"+phone, time.Now().Unix(), 60*5*time.Second).Result() + if err != nil { + return false, errors.New("验证码发送失败,请您稍后再试") + } + } + + // 设置验证码有效期 + _, err = global.Redis.Set(context.Background(), "login_code_"+phone, code, 60*5*time.Second).Result() + if err != nil { + return false, errors.New("验证码发送失败,请您稍后再试") + } + + return true, nil +} + +// GetUserIP 获取用户ip +func (r *PublicService) GetUserIP(h *http.Request) string { + forwarded := h.Header.Get("X-FORWARDED-FOR") + if forwarded != "" { + return forwarded + } + return h.RemoteAddr +} diff --git a/api/service/Question.go b/api/service/Question.go new file mode 100644 index 0000000..a5834ae --- /dev/null +++ b/api/service/Question.go @@ -0,0 +1,230 @@ +package service + +import ( + "errors" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "time" +) + +type QuestionService struct { +} + +// GetHotList 获取算一算热榜-人气数最高的9个 +func (r *QuestionService) GetHotList() (m []*model.Question, err error) { + questionDao := dao.QuestionDao{} + + maps := make(map[string]interface{}) + maps["question_status"] = 1 + maps["is_hide"] = 0 + questions, err := questionDao.GetQuestionOrderLimitList(maps, "click_count desc", 9) + if err != nil { + return nil, err + } + + return questions, nil +} + +// GetRecommendList 获取为你推荐-后台指定的推广 +func (r *QuestionService) GetRecommendList() (m []*model.Question, err error) { + questionDao := dao.QuestionDao{} + + maps := make(map[string]interface{}) + maps["question_status"] = 1 + maps["is_hide"] = 0 + maps["is_recommend"] = 1 + questions, err := questionDao.GetQuestionList(maps) + if err != nil { + return nil, err + } + + return questions, nil +} + +// GetGuessUserLIkeList 获取猜你喜欢-暂用公众参与过的最新算一算,至多显示3个。若未参与,则指定或者随机显示3个 +func (r *QuestionService) GetGuessUserLIkeList(userId int64) (m []*model.Question, err error) { + orderSingleDao := dao.OrderSingleDao{} + questionDao := dao.QuestionDao{} + + var questions []*model.Question + + if userId != 0 { + maps := make(map[string]interface{}) + maps["user_id"] = userId + orderSingles, err := orderSingleDao.GetOrderSingleOrderList(maps, "created_at desc", 3) + if err != nil { + return nil, err + } + + // 参与过 + if len(orderSingles) > 0 { + for i, single := range orderSingles { + questions[i] = single.Question + } + } + } + + // 未参与过/未指定用户 + if len(questions) == 0 { + maps := make(map[string]interface{}) + maps["question_status"] = 1 + maps["is_hide"] = 0 + questions, err = questionDao.GetQuestionListRand(maps, 3) + if err != nil { + return nil, err + } + } + + return questions, nil +} + +// CheckUserCollectionQuestion 检测问题是否被用户收藏 +func (r *QuestionService) CheckUserCollectionQuestion(userId, questionId int64) (bool, error) { + userCollectionDao := dao.UserCollectionDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["question_id"] = questionId + userCollection, err := userCollectionDao.GetUserCollection(maps) + if userCollection == nil { + return false, err + } + + return true, nil +} + +// CheckUserBuyQuestion 检测用户是否购买过该问题 +func (r *QuestionService) CheckUserBuyQuestion(userId, questionId int64) bool { + orderSingleDao := dao.OrderSingleDao{} + orderSingle, _ := orderSingleDao.GetUserFirstTimeBuyOrderSingle(userId, questionId) + if orderSingle == nil { + return false + } + + return true +} + +// GetUserFirstTimeBuyPrice 获取用户首次购买价格 +func (r *QuestionService) GetUserFirstTimeBuyPrice(userId, questionId int64) (f *float64, err error) { + // 检测用户是否购买过该问题 + isFirstBuy := r.CheckUserBuyQuestion(userId, questionId) + if isFirstBuy == false { + // 未购买过 + systemSingleDao := dao.SystemSingleDao{} + + maps := make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + return nil, err + } + + return &systemSingle.FirstTimePrice, nil + } + + return nil, nil +} + +// GetQuestionBuyCount 获取问题被购买数量 +func (r *QuestionService) GetQuestionBuyCount(userId, questionId int64) (c int, err error) { + // 未购买过 + orderSingleDao := dao.OrderSingleDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["question_id"] = questionId + maps["order_status"] = 2 + maps["refund_status"] = 0 + buyCount, err := orderSingleDao.GetOrderSingleCount(maps) + if err != nil { + return 0, err + } + + return int(buyCount), nil +} + +// GetUserBuyPrice 获取问题最终价格 +func (r *QuestionService) GetUserBuyPrice(userId, questionId int64) (p *float64, err error) { + // 获取问题详情 + questionDao := dao.QuestionDao{} + question, err := questionDao.GetQuestionById(questionId) + if err != nil { + return nil, errors.New("题目异常") + } + + // 检测用户是否购买过该问题 + isFirstBuy := r.CheckUserBuyQuestion(userId, questionId) + if isFirstBuy == false { + // 未购买过 + systemSingleDao := dao.SystemSingleDao{} + + maps := make(map[string]interface{}) + systemSingle, err := systemSingleDao.GetSystemSingle(maps) + if err != nil { + return nil, err + } + + p = &systemSingle.FirstTimePrice + } + + // 处理问题优惠价格 + if p == nil { + p = r.HandleQuestionDiscountPrice(question.DiscountPrice, question.DiscountEndTime) + } + + if p == nil { + p = &question.Price + } + + return p, nil +} + +// CheckQuestion 检测题目 +func (r *QuestionService) CheckQuestion(m *model.Question) (bool, error) { + if m.QuestionStatus != 1 { + return false, errors.New("题目异常") + } + + if m.IsHide != 0 { + return false, errors.New("题目异常") + } + + return true, nil +} + +// HandleQuestionDiscountPrice 处理问题优惠价格 +func (r *QuestionService) HandleQuestionDiscountPrice(discountPrice *float64, discountEndTime *time.Time) (p *float64) { + // 优惠价格 + if discountPrice != nil { + // 检测是否超出优惠时间 + now := time.Now() + if discountEndTime.Before(now) { + p = nil + } else { + p = discountPrice + } + } + + return p +} + +// GetQuestionBaseClass 获取问题关联分类 +func (r *QuestionService) GetQuestionBaseClass(questionId int64) (g []*dto.BaseClassDto, err error) { + questionClassDao := dao.QuestionClassDao{} + questionClass, _ := questionClassDao.GetQuestionClassListByQuestionId(questionId) + if len(questionClass) > 0 { + baseClassDao := dao.BaseClassDao{} + for _, class := range questionClass { + baseClass, err := baseClassDao.GetBaseClassById(class.ClassId) + if err != nil { + return nil, errors.New("题目异常") + } + + baseClassDto := dto.GetBaseClassDto(baseClass) + + g = append(g, baseClassDto) + } + } + + return g, nil +} diff --git a/api/service/SystemMember.go b/api/service/SystemMember.go new file mode 100644 index 0000000..1d7df23 --- /dev/null +++ b/api/service/SystemMember.go @@ -0,0 +1,17 @@ +package service + +import "hepa-calc-admin-api/api/model" + +type SystemMemberService struct { +} + +// GetSystemMemberBuyPrice 获取会员购买价格 +func (r *SystemMemberService) GetSystemMemberBuyPrice(m *model.SystemMember) (p float64) { + p = m.Price + + if m.DiscountPrice != nil { + p = *m.DiscountPrice + } + + return p +} diff --git a/api/service/User.go b/api/service/User.go new file mode 100644 index 0000000..8ffb9f3 --- /dev/null +++ b/api/service/User.go @@ -0,0 +1,107 @@ +package service + +import ( + "errors" + "fmt" + "gorm.io/gorm" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/extend/aliyun" + "io" + "math/rand" + "net/http" + "time" +) + +type UserService struct { +} + +// HandleUserAvatar 处理用户头像 +func (r *UserService) HandleUserAvatar(wxAvatar string) (avatar string, err error) { + if wxAvatar == "" { + return "", nil + } + + // 发送GET请求 + resp, err := http.Get(wxAvatar) + if err != nil { + return "", err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != 200 { + return "", errors.New("请求失败") + } + + // 读取响应体 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + // 设置文件名字 + now := time.Now() + dateTimeString := now.Format("20060102150405") // 当前时间字符串 + rand.New(rand.NewSource(time.Now().UnixNano())) // 设置随机数 + ossPath := "user/avatar/" + dateTimeString + fmt.Sprintf("%d", rand.Intn(9000)+1000) + ".png" + + // 上传oss + _, err = aliyun.PutObjectByte(ossPath, respBody) + if err != nil { + return "", err + } + + ossPath = "/" + ossPath + + return ossPath, nil +} + +// CheckUserMember 检测用户会员 +func (r *UserService) CheckUserMember(user *model.User) bool { + if user.IsMember == 0 { + return false + } + + now := time.Now() + if user.MemberExpireDate.Before(now) { + return false + } + + return true +} + +// CheckUserBuyOrderMember 检测用户是否购买过会员 +func (r *UserService) CheckUserBuyOrderMember(userId int64) bool { + orderMemberDao := dao.OrderMemberDao{} + orderMember, _ := orderMemberDao.GetUserFirstTimeBuyOrderMember(userId) + if orderMember == nil { + return false + } + + return true +} + +// AddUserMemberValidDate 增加用户会员过期时间 +func (r *UserService) AddUserMemberValidDate(tx *gorm.DB, user *model.User, d int) bool { + userData := make(map[string]interface{}) + if user.IsMember == 0 { + userData["is_member"] = 1 + } + + if user.MemberExpireDate == nil { + userData["is_member"] = time.Now().Format("2006-01-02 15:04:05") + } else { + userData["is_member"] = user.MemberExpireDate.Add(time.Duration(d) * 24 * time.Hour) + } + + userDao := dao.UserDao{} + err := userDao.EditUserById(tx, user.UserId, userData) + if err != nil { + return false + } + + return true +} diff --git a/api/service/UserCollection.go b/api/service/UserCollection.go new file mode 100644 index 0000000..fe57735 --- /dev/null +++ b/api/service/UserCollection.go @@ -0,0 +1,91 @@ +package service + +import ( + "errors" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/model" + "hepa-calc-admin-api/global" +) + +type UserCollectionService struct { +} + +// GetUserCollectionQuestionStatus 检测用户收藏状态 +func (r *UserCollectionService) GetUserCollectionQuestionStatus(userId, questionId int64) bool { + userCollectionDao := dao.UserCollectionDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["question_id"] = questionId + userCollection, _ := userCollectionDao.GetUserCollection(maps) + if userCollection == nil { + return false + } + + return true +} + +// PutUserCollection 收藏题目 +func (r *UserCollectionService) PutUserCollection(userId, questionId int64) (bool, error) { + // 检测用户收藏状态 + IsCollection := r.GetUserCollectionQuestionStatus(userId, questionId) + if IsCollection == true { + // 已收藏 + return true, nil + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + userCollection := &model.UserCollection{ + UserId: userId, + QuestionId: questionId, + } + + userCollectionDao := dao.UserCollectionDao{} + userCollection, err := userCollectionDao.AddUserCollection(tx, userCollection) + if err != nil { + tx.Rollback() + return false, errors.New("收藏失败") + } + + tx.Commit() + return true, nil +} + +// PutUserCollectionCancel 取消收藏题目 +func (r *UserCollectionService) PutUserCollectionCancel(userId, questionId int64) (bool, error) { + // 检测用户收藏状态 + IsCollection := r.GetUserCollectionQuestionStatus(userId, questionId) + if IsCollection == false { + // 已收藏 + return true, nil + } + + // 开始事务 + tx := global.Db.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + userCollectionDao := dao.UserCollectionDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["question_id"] = questionId + err := userCollectionDao.DeleteUserCollection(tx, maps) + if err != nil { + tx.Rollback() + return false, errors.New("取消收藏失败") + } + + tx.Commit() + return true, nil +} diff --git a/api/service/UserCoupon.go b/api/service/UserCoupon.go new file mode 100644 index 0000000..67f184d --- /dev/null +++ b/api/service/UserCoupon.go @@ -0,0 +1,208 @@ +package service + +import ( + "errors" + "gorm.io/gorm" + "hepa-calc-admin-api/api/dao" + "hepa-calc-admin-api/api/dto" + "hepa-calc-admin-api/api/model" + "time" +) + +type UserCouponService struct { +} + +// CheckUserCoupon 检测用户优惠卷 +// orderType:类型(1:单项 2:会员) +func (r *UserCouponService) CheckUserCoupon(m *model.UserCoupon, id int64, orderType int, amountTotal float64) (bool, error) { + if m.UserCouponStatus == 1 { + return false, errors.New("优惠卷异常") + } + + if m.UserCouponStatus == 2 { + return false, errors.New("优惠卷已过期,无法使用") + } + + now := time.Now() + validEndTime := time.Time(m.ValidEndTime) + if validEndTime.Before(now) { + return false, errors.New("优惠卷已过期,无法使用") + } + + if m.Coupon == nil { + return false, errors.New("优惠卷异常") + } + + // 检测优惠卷状态 + if m.Coupon.CouponStatus == 2 { + return false, errors.New("优惠卷已失效,无法使用") + } + + if m.Coupon.CouponStatus == 3 { + return false, errors.New("优惠卷无法使用") + } + + if m.Coupon.CouponStatus == 4 { + return false, errors.New("优惠卷异常,无法使用") + } + + // 检测价格 + if m.Coupon.CouponType == 2 { + if *m.Coupon.WithAmount > amountTotal { + return false, errors.New("优惠卷不符合满减金额标准,无法使用") + } + } + + // 单项 + if orderType == 1 { + if m.Coupon.ApplicationScope != 1 && m.Coupon.ApplicationScope != 2 { + return false, errors.New("优惠卷无法使用") + } + + if id != *m.Coupon.QuestionId { + return false, errors.New("优惠卷无法使用") + } + } + + // 会员 + if orderType == 2 { + if m.Coupon.ApplicationScope != 1 && m.Coupon.ApplicationScope != 3 { + return false, errors.New("优惠卷无法使用") + } + + if id != *m.Coupon.SystemMemberId { + return false, errors.New("优惠卷无法使用") + } + } + + // 检测优惠劵过期时间 + if m.Coupon.ValidType == 1 { + validEndTime = time.Time(*m.Coupon.ValidEndTime) + if validEndTime.Before(now) { + return false, errors.New("优惠卷已过期,无法使用") + } + } + + return true, nil +} + +// ReturnUserCoupon 退还优惠卷 +func (r *UserCouponService) ReturnUserCoupon(tx *gorm.DB, userCouponId int64) bool { + // 获取优惠卷数据 + UserCouponDao := dao.UserCouponDao{} + userCoupon, err := UserCouponDao.GetUserCouponPreloadById(userCouponId) + if err != nil { + // 无该优惠卷数据,无需处理 + return true + } + + userCouponDao := dao.UserCouponDao{} + userCouponData := make(map[string]interface{}) + + // 检测优惠卷过期时间。判断是否需要退还 + now := time.Now() + validEndTime := time.Time(userCoupon.ValidEndTime) + if validEndTime.Before(now) { + userCouponData["user_coupon_status"] = 3 + } else { + userCouponData["user_coupon_status"] = 0 + } + + userCouponData["coupon_use_date"] = nil + err = userCouponDao.EditUserCouponById(tx, userCoupon.UserCouponId, userCouponData) + if err != nil { + return false + } + + return true +} + +// GetUserUsableQuestionCoupon 获取用户可使用优惠卷-单项 +func (r *UserCouponService) GetUserUsableQuestionCoupon(userId, questionId int64, amountTotal float64) (g []*dto.UserCouponDto, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + // 检测用户会员 + userService := &UserService{} + isMember := userService.CheckUserMember(user) + if isMember == true { + // 会员无需使用优惠卷 + return nil, nil + } + + // 获取用户优惠卷 + UserCouponDao := dao.UserCouponDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["user_coupon_status"] = 0 + userCoupons, err := UserCouponDao.GetUserCouponPreloadList(maps) + if err != nil { + return nil, errors.New("优惠券异常") + } + + //定义返回数据 + var responses []*model.UserCoupon + + for _, userCoupon := range userCoupons { + isCanUse, err := r.CheckUserCoupon(userCoupon, questionId, 1, amountTotal) + if err != nil || isCanUse == false { + continue + } + + responses = append(responses, userCoupon) + } + + g = dto.GetUserCouponListDto(responses) + + return g, nil +} + +// GetUserUsableMemberCoupon 获取用户可使用优惠卷-会员 +func (r *UserCouponService) GetUserUsableMemberCoupon(userId, systemMemberId int64, amountTotal float64) (g []*dto.UserCouponDto, err error) { + // 获取用户数据 + userDao := dao.UserDao{} + user, err := userDao.GetUserById(userId) + if err != nil || user == nil { + return nil, errors.New("用户错误") + } + + // 检测用户会员 + userService := &UserService{} + isMember := userService.CheckUserMember(user) + if isMember == true { + // 会员无需使用优惠卷 + return nil, nil + } + + // 获取用户优惠卷 + UserCouponDao := dao.UserCouponDao{} + + maps := make(map[string]interface{}) + maps["user_id"] = userId + maps["user_coupon_status"] = 0 + userCoupons, err := UserCouponDao.GetUserCouponPreloadList(maps) + if err != nil { + return nil, errors.New("优惠券异常") + } + + //定义返回数据 + var responses []*model.UserCoupon + + for _, userCoupon := range userCoupons { + isCanUse, err := r.CheckUserCoupon(userCoupon, systemMemberId, 2, amountTotal) + if err != nil || isCanUse == false { + continue + } + + responses = append(responses, userCoupon) + } + + g = dto.GetUserCouponListDto(responses) + + return g, nil +} diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..374c7ca --- /dev/null +++ b/config.yaml @@ -0,0 +1,69 @@ +port: 8598 # 启动端口 + +env: dev # 环境配置 + +snowflake: 1 # 雪花算法机器id + +# [数据库配置] +mysql: + host: '42.193.16.243' + username: root + password: 'gdxz123456^%$d' + port: 30001 + db-name: hepa_calc + max-idle-cons: 5 + max-open-cons: 20 + debug: true + +log: + file-path: "./log/" + file-name: "hepa-calc-admin-api.log" + +# [redis] +redis: + host: '139.155.127.177' + port: 30002 + password: gdxz2022&dj. + pool-size: 100 + db: 3 + +# [jwt] +jwt: + sign-key: 123456899 # 私钥 + ttl : 72 # 过期时间 小时 + algo : HS256 # 加密方式 + +oss: + oss-access-key: LTAI5tKmFrVCghcxX7yHyGhm + oss-access-key-secret: q1aiIZCJJuf92YbKk2cSXnPES4zx26 + oss-bucket: dev-knowledge + oss-endpoint: oss-cn-beijing.aliyuncs.com + oss-custom-domain-name: https://dev-knowledge.oss-cn-beijing.aliyuncs.com + +# [阿里大鱼短信] +dysms: + dysms-access-key: LTAI4GGygjsKhyBwvvC3CghV + dysms-access-secret: rcx7lO9kQxG10m8NqNPEfEtT9IS8EI + +# [微信] +wechat: + app-id: wx68affaa9d23528f8 + app-secret: 2963c90242ddb2421c939591ad9e903d + pay-notify-url: callback/wxpay/single + refund-notify-url: callback/wxpay/inquiry/refund + refund-notify-domain: https://dev-hepa.igandan.com/api/ + pay-1281030301: + mch-id: 1281030301 + v3-api-secret: sB2tCkT70uwEy7cQCu1llA6nilTbek6F + mch-certificate-serial-number: 3D1685894CEDD41753A470211F975D40AE1375F5 + platform-certs: extend/weChat/certs/1281030301/wechatpay_5B5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem + private-key: extend/weChat/certs/1281030301/apiclient_key.pem + certificate: extend/weChat/certs/1281030301/apiclient_cert.pem + +# [rabbitMq] +amqp: + host: 42.193.16.243 + port: 5672 + user: gdxz_2022rabbitmq + password: qwr2p&¥e@3.2p + vhost: gdxz_hepa \ No newline at end of file diff --git a/config/amqp.go b/config/amqp.go new file mode 100644 index 0000000..f7d1c63 --- /dev/null +++ b/config/amqp.go @@ -0,0 +1,9 @@ +package config + +type Amqp struct { + Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 + Port int `mapstructure:"port" json:"port" yaml:"port"` // 服务器端口 + Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码 + User string `mapstructure:"user" json:"user" yaml:"user"` + Vhost string `mapstructure:"vhost" json:"vhost" yaml:"vhost"` +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..4a24ee6 --- /dev/null +++ b/config/config.go @@ -0,0 +1,17 @@ +package config + +var C Config + +type Config struct { + Port int `mapstructure:"port" json:"port" yaml:"port"` + Env string `mapstructure:"env" json:"Env" yaml:"Env"` + Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"` + Log Log `mapstructure:"log" json:"log" yaml:"log"` + Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"` + Jwt Jwt `mapstructure:"jwt" json:"jwt" yaml:"jwt"` + Oss Oss `mapstructure:"oss" json:"oss" yaml:"oss"` + Snowflake int64 `mapstructure:"snowflake" json:"snowflake" yaml:"snowflake"` + Dysms Dysms `mapstructure:"dysms" json:"dysms" yaml:"dysms"` + Wechat Wechat `mapstructure:"wechat" json:"wechat" yaml:"wechat"` + Amqp Amqp `mapstructure:"amqp" json:"amqp" yaml:"amqp"` +} diff --git a/config/dysms.go b/config/dysms.go new file mode 100644 index 0000000..7d02d49 --- /dev/null +++ b/config/dysms.go @@ -0,0 +1,6 @@ +package config + +type Dysms struct { + DysmsAccessKey string `mapstructure:"dysms-access-key" json:"dysms-access-key" yaml:"dysms-access-key"` + DysmsAccessSecret string `mapstructure:"dysms-access-secret" json:"dysms-access-secret" yaml:"dysms-access-secret"` +} diff --git a/config/jwt.go b/config/jwt.go new file mode 100644 index 0000000..9f59bed --- /dev/null +++ b/config/jwt.go @@ -0,0 +1,7 @@ +package config + +type Jwt struct { + SignKey string `mapstructure:"sign-key" json:"sign-key" yaml:"sign-key"` // 私钥 + Ttl int `mapstructure:"ttl" json:"ttl" yaml:"ttl"` // 过期时间 小时 + Algo string `mapstructure:"algo" json:"algo" yaml:"algo"` // 加密方式 +} diff --git a/config/log.go b/config/log.go new file mode 100644 index 0000000..ae86ff5 --- /dev/null +++ b/config/log.go @@ -0,0 +1,6 @@ +package config + +type Log struct { + FilePath string `mapstructure:"file-path" json:"file-path" yaml:"file-path"` // 日志目录 + FileName string `mapstructure:"file-name" json:"file-name" yaml:"file-name"` // 日志名称 +} diff --git a/config/mysql.go b/config/mysql.go new file mode 100644 index 0000000..5135c8e --- /dev/null +++ b/config/mysql.go @@ -0,0 +1,12 @@ +package config + +type Mysql struct { + Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 + Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 + DbName string `mapstructure:"db-name" json:"db-name" yaml:"db-name"` // 数据库名 + Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名 + Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码 + MaxIdleConns int `mapstructure:"max-idle-cons" json:"MaxIdleConns" yaml:"max-idle-cons"` // 空闲中的最大连接数 + MaxOpenConns int `mapstructure:"max-open-cons" json:"MaxOpenConns" yaml:"max-open-cons"` // 打开到数据库的最大连接数 + Debug bool `mapstructure:"debug" json:"debug" yaml:"debug"` // 是否开启Gorm全局日志 +} diff --git a/config/oss.go b/config/oss.go new file mode 100644 index 0000000..7c508b4 --- /dev/null +++ b/config/oss.go @@ -0,0 +1,9 @@ +package config + +type Oss struct { + OssAccessKey string `mapstructure:"oss-access-key" json:"oss-access-key" yaml:"oss-access-key"` + OssAccessKeySecret string `mapstructure:"oss-access-key-secret" json:"oss-access-key-secret" yaml:"oss-access-key-secret"` + OssBucket string `mapstructure:"oss-bucket" json:"oss-bucket" yaml:"oss-bucket"` + OssEndpoint string `mapstructure:"oss-endpoint" json:"oss-endpoint" yaml:"oss-endpoint"` + OssCustomDomainName string `mapstructure:"oss-custom-domain-name" json:"oss-custom-domain-name" yaml:"oss-custom-domain-name"` +} diff --git a/config/redis.go b/config/redis.go new file mode 100644 index 0000000..324aaba --- /dev/null +++ b/config/redis.go @@ -0,0 +1,9 @@ +package config + +type Redis struct { + Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 + Port int `mapstructure:"port" json:"port" yaml:"port"` // 服务器端口 + Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码 + PoolSize int `mapstructure:"pool-size" json:"pool-size" yaml:"pool-size"` // 连接池大小 + Db int `mapstructure:"db" json:"db" yaml:"db"` // 数据库 +} diff --git a/config/wechat.go b/config/wechat.go new file mode 100644 index 0000000..d03f879 --- /dev/null +++ b/config/wechat.go @@ -0,0 +1,20 @@ +package config + +type Wechat struct { + AppId string `mapstructure:"app-id" json:"app-id" yaml:"patient-app-id"` + AppSecret string `mapstructure:"app-secret" json:"app-secret" yaml:"app-secret"` + PayNotifyUrl string `mapstructure:"pay-notify-url" json:"pay-notify-url" yaml:"pay-notify-url"` + RefundNotifyDomain string `mapstructure:"refund-notify-domain" json:"refund-notify-domain" yaml:"refund-notify-domain"` // 回调域名 + RefundNotifyUrl string `mapstructure:"refund-notify-url" json:"refund-notify-url" yaml:"refund-notify-url"` + Pay1281030301 Pay1281030301 `mapstructure:"pay-1281030301" json:"pay-1281030301" yaml:"pay-1281030301"` +} + +// Pay1281030301 app +type Pay1281030301 struct { + MchId string `mapstructure:"mch-id" json:"mch-id" yaml:"mch-id"` // 商户号 + V3ApiSecret string `mapstructure:"v3-api-secret" json:"v3-api-secret" yaml:"v3-api-secret"` // 商户APIv3密钥 + MchCertificateSerialNumber string `mapstructure:"mch-certificate-serial-number" json:"mch-certificate-serial-number" yaml:"mch-certificate-serial-number"` // 商户证书序列号 + PlatformCerts string `mapstructure:"platform-certs" json:"platform-certs" yaml:"platform-certs"` // 平台证书 + PrivateKey string `mapstructure:"private-key" json:"private-key" yaml:"private-key"` + Certificate string `mapstructure:"certificate" json:"certificate" yaml:"certificate"` +} diff --git a/consts/http.go b/consts/http.go new file mode 100644 index 0000000..00614e5 --- /dev/null +++ b/consts/http.go @@ -0,0 +1,24 @@ +package consts + +// 业务状态码 +const ( + HttpSuccess = 200 // 成功 + + HttpError = -1 // 失败 + + UserStatusError = 201 // 用户状态异常 + + ClientHttpError = 400 // 错误请求 + + ClientHttpUnauthorized = 401 // 未授权 + + HttpProhibit = 403 // 禁止请求 + + ClientHttpNotFound = 404 // 资源未找到 + + TokenError = 405 // token错误/无效 + + TokenExptired = 406 // token过期 + + ServerError = 500 // 服务器异常 +) diff --git a/core/cron.go b/core/cron.go new file mode 100644 index 0000000..d6e5966 --- /dev/null +++ b/core/cron.go @@ -0,0 +1,15 @@ +package core + +import ( + "fmt" + "github.com/robfig/cron/v3" +) + +func StartCron() { + c := cron.New(cron.WithSeconds()) + + // 启动定时任务调度器 + c.Start() + + fmt.Println("初始化定时器成功......") +} diff --git a/core/logrus.go b/core/logrus.go new file mode 100644 index 0000000..ccb8b28 --- /dev/null +++ b/core/logrus.go @@ -0,0 +1,46 @@ +package core + +import ( + "github.com/sirupsen/logrus" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" + "os" + "path" + "path/filepath" +) + +// Logrus 日志记录到文件 +func Logrus() *logrus.Logger { + // 日志文件 + fileName := path.Join(config.C.Log.FilePath, config.C.Log.FileName) + + // 获取文件夹路径 + dirPath := filepath.Dir(fileName) + + // 创建文件夹(如果不存在) + err := os.MkdirAll(dirPath, os.ModePerm) + if err != nil { + panic("初始化日志文件失败") + } + + // 写入文件 + src, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm) + if err != nil { + panic("初始化日志文件失败") + } + + global.Logger = logrus.New() + + // 设置输出 + global.Logger.Out = src + + // 设置日志级别 + global.Logger.SetLevel(logrus.DebugLevel) + + // 设置日志格式 + global.Logger.SetFormatter(&logrus.TextFormatter{ + TimestampFormat: "2006-01-02 15:04:05", + }) + + return global.Logger +} diff --git a/core/mysql.go b/core/mysql.go new file mode 100644 index 0000000..9d7b146 --- /dev/null +++ b/core/mysql.go @@ -0,0 +1,56 @@ +package core + +import ( + "fmt" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" + "time" +) + +func Mysql() { + var err error + + m := config.C.Mysql + dsn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", m.Username, + m.Password, m.Host, m.Port, m.DbName, "10s") + + // newLogger := logger.New( + // global.Logger, + // logger.Config{ + // SlowThreshold: time.Second, // Slow SQL threshold + // LogLevel: logger.Info, // Log level + // IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger + // ParameterizedQueries: false, // Don't include params in the SQL log + // Colorful: false, // Disable color + // }, + // ) + + global.Db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Info), + // Logger: newLogger, + }) + + if err != nil { + fmt.Println(err.Error()) + panic(err.Error()) + } + + sqlDB, _ := global.Db.DB() + + // SetMaxIdleConns 设置空闲连接池中连接的最大数量 + sqlDB.SetMaxIdleConns(m.MaxIdleConns) + + // SetMaxOpenConns 设置打开数据库连接的最大数量。 + sqlDB.SetMaxOpenConns(m.MaxOpenConns) + + // SetConnMaxLifetime 设置了连接可复用的最大时间。 + sqlDB.SetConnMaxLifetime(time.Hour) + + // 调试模式 + //Db.LogMode(m.Debug) // 打印sql + // Db.SingularTable(true) // 全局禁用表名复数 + fmt.Println("初始化数据库成功......") +} diff --git a/core/rabbitMq.go b/core/rabbitMq.go new file mode 100644 index 0000000..c0689e6 --- /dev/null +++ b/core/rabbitMq.go @@ -0,0 +1,45 @@ +package core + +import ( + "fmt" + "hepa-calc-admin-api/extend/rabbitMq" + "hepa-calc-admin-api/utils" +) + +// StartRabbitMq 启动rabbitmq +func StartRabbitMq() { + err := rabbitMq.NewRabbitMQClient() + if err != nil { + utils.LogJsonErr("启动rabbitmq:", err.Error()) + panic(err.Error()) + } + + // 保持连接 + go rabbitMq.HandleReconnect() + + fmt.Println("初始化rabbitMq成功......") +} + +// StartRabbitMqConsume 启动消费者端 +func StartRabbitMqConsume() { + +} + +// startConsumer 启动指定的消费者协程 +func startConsumer(s rabbitMq.ConsumeS) { + go func() { + defer func() { + if r := recover(); r != nil { + utils.LogJsonErr(fmt.Sprintf("消费者协程崩溃 - %s", s.QueueName), fmt.Errorf("%v", r)) + // 重新启动客户端 + StartRabbitMq() + } + }() + + err := s.Consume() + if err != nil { + fmt.Println(err.Error()) + utils.LogJsonErr(fmt.Sprintf("启动消费者队列失败 - %s", s.QueueName), err) + } + }() +} diff --git a/core/redis.go b/core/redis.go new file mode 100644 index 0000000..6e9e838 --- /dev/null +++ b/core/redis.go @@ -0,0 +1,25 @@ +package core + +import ( + "context" + "fmt" + "github.com/go-redis/redis/v8" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" + "strconv" +) + +// Redis redis缓存 +func Redis() { + global.Redis = redis.NewClient(&redis.Options{ + Addr: config.C.Redis.Host + ":" + strconv.Itoa(config.C.Redis.Port), + Password: config.C.Redis.Password, // no password set + DB: config.C.Redis.Db, // use default DB + PoolSize: config.C.Redis.PoolSize, + }) + _, err := global.Redis.Ping(context.Background()).Result() + if err != nil { + panic("redis初始化失败! " + err.Error()) + } + fmt.Println("初始化redis成功......") +} diff --git a/core/snowflake.go b/core/snowflake.go new file mode 100644 index 0000000..b2e5f10 --- /dev/null +++ b/core/snowflake.go @@ -0,0 +1,21 @@ +package core + +import ( + "fmt" + "github.com/bwmarrin/snowflake" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" +) + +// Snowflake 雪花算法 +func Snowflake() { + // 创建雪花算法实例 + node, err := snowflake.NewNode(config.C.Snowflake) + if err != nil { + panic("snowflake初始化失败! " + err.Error()) + } + + global.Snowflake = node + + fmt.Println("初始化snowflake成功......") +} diff --git a/core/validator.go b/core/validator.go new file mode 100644 index 0000000..03f524d --- /dev/null +++ b/core/validator.go @@ -0,0 +1,95 @@ +package core + +import ( + "github.com/go-playground/locales/zh" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" + zhTranslations "github.com/go-playground/validator/v10/translations/zh" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "reflect" +) + +// Validator 验证器 +func Validator() { + chzh := zh.New() + uni := ut.New(chzh, chzh) + global.Trans, _ = uni.GetTranslator("zh") + + global.Validate = validator.New() + + // 通过label标签返回自定义错误内容 + global.Validate.RegisterTagNameFunc(func(field reflect.StructField) string { + label := field.Tag.Get("label") + if label == "" { + return field.Name + } + return label + }) + _ = zhTranslations.RegisterDefaultTranslations(global.Validate, global.Trans) + // 注册自定义函数和标签 + // 手机号验证 + _ = global.Validate.RegisterValidation("Mobile", mobile) // 注册自定义函数,前一个参数是struct里tag自定义,后一个参数是自定义的函数 + + // 自定义required错误内容 + _ = global.Validate.RegisterTranslation("required", global.Trans, func(ut ut.Translator) error { + return ut.Add("required", "{0}为必填字段!", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("required", fe.Field()) + return t + }) + + // 自定义max错误内容 + _ = global.Validate.RegisterTranslation("max", global.Trans, func(ut ut.Translator) error { + return ut.Add("max", "{0}超出最大长度", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("max", fe.Field()) + return t + }) + + // 自定义min错误内容 + _ = global.Validate.RegisterTranslation("min", global.Trans, func(ut ut.Translator) error { + return ut.Add("min", "{0}超出最小长度", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("min", fe.Field()) + return t + }) + + // 自定义lt错误内容 + _ = global.Validate.RegisterTranslation("lt", global.Trans, func(ut ut.Translator) error { + return ut.Add("lt", "{0}超出最大值", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("lt", fe.Field()) + return t + }) + + // 自定义min错误内容 + _ = global.Validate.RegisterTranslation("gt", global.Trans, func(ut ut.Translator) error { + return ut.Add("gt", "{0}不满足最小值", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("gt", fe.Field()) + return t + }) + + // 自定义email错误内容 + _ = global.Validate.RegisterTranslation("email", global.Trans, func(ut ut.Translator) error { + return ut.Add("email", "{0}邮件格式错误", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("email", fe.Field()) + return t + }) + + // 自定义mobile错误内容 + _ = global.Validate.RegisterTranslation("Mobile", global.Trans, func(ut ut.Translator) error { + return ut.Add("mobile", "手机号格式错误", false) // see universal-translator for details + }, func(ut ut.Translator, fe validator.FieldError) string { + t, _ := ut.T("mobile", fe.Field()) + return t + }) + +} + +// 自定义手机号验证 +func mobile(fl validator.FieldLevel) bool { + return utils.RegexpMobile(fl.Field().String()) +} diff --git a/core/viper.go b/core/viper.go new file mode 100644 index 0000000..7155201 --- /dev/null +++ b/core/viper.go @@ -0,0 +1,41 @@ +package core + +import ( + "fmt" + "github.com/fsnotify/fsnotify" + "github.com/spf13/viper" + "hepa-calc-admin-api/config" +) + +// Viper 初始化配置文件 +func Viper() { + // 如需增加环境判断 在此处增加 + // 可根据 命令行 > 环境变量 > 默认值 等优先级进行判别读取 + viper.New() + viper.SetConfigName("config") + viper.AddConfigPath("./") + viper.SetConfigType("yaml") + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + panic("未找到该文件") + } else { + panic("读取失败") + } + } + + // 将读取的配置信息保存至全局变量Conf + if err := viper.Unmarshal(&config.C); err != nil { + panic(fmt.Errorf("解析配置文件失败, err:%s \n", err)) + } + + // 自动监听配置修改 + viper.WatchConfig() + + // 配置文件发生变化后同步到全局变量Conf + viper.OnConfigChange(func(e fsnotify.Event) { + fmt.Println("config file changed:", e.Name) + if err := viper.Unmarshal(&config.C); err != nil { + panic(fmt.Errorf("重载配置文件失败, err:%s \n", err)) + } + }) +} diff --git a/extend/aliyun/dysms.go b/extend/aliyun/dysms.go new file mode 100644 index 0000000..29b3a33 --- /dev/null +++ b/extend/aliyun/dysms.go @@ -0,0 +1,104 @@ +// Package aliyun 短信 +package aliyun + +import ( + "encoding/json" + "errors" + openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client" + dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v3/client" + util "github.com/alibabacloud-go/tea-utils/v2/service" + "github.com/alibabacloud-go/tea/tea" + "hepa-calc-admin-api/config" +) + +func createClient() (_result *dysmsapi20170525.Client, _err error) { + accessKeyId := config.C.Dysms.DysmsAccessKey + accessKeySecret := config.C.Dysms.DysmsAccessSecret + + openapiConfig := &openapi.Config{ + // 必填,您的 AccessKey ID + AccessKeyId: &accessKeyId, + // 必填,您的 AccessKey Secret + AccessKeySecret: &accessKeySecret, + } + // Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi + openapiConfig.Endpoint = tea.String("dysmsapi.aliyuncs.com") + _result = &dysmsapi20170525.Client{} + _result, _err = dysmsapi20170525.NewClient(openapiConfig) + return _result, _err +} + +// SendSms 发送短信 +func SendSms(phoneNumber, templateCode, sceneDesc string, templateParam map[string]interface{}) error { + client, err := createClient() + if err != nil { + return err + } + + params, err := json.Marshal(templateParam) + if err != nil { + return err + } + + sendSmsRequest := &dysmsapi20170525.SendSmsRequest{ + PhoneNumbers: tea.String(phoneNumber), + SignName: tea.String("肝胆相照"), + TemplateCode: tea.String(templateCode), + TemplateParam: tea.String(string(params)), + } + + tryErr := func() (e error) { + defer func() { + if r := tea.Recover(recover()); r != nil { + e = r + } + }() + + // 初始化运行时配置。 + runtime := &util.RuntimeOptions{} + // 读取超时 + runtime.SetReadTimeout(10000) + // 连接超时 + runtime.SetConnectTimeout(5000) + + // 复制代码运行请自行打印 API 的返回值 + response, err := client.SendSms(sendSmsRequest) + if err != nil { + return err + } + + if response.Body == nil { + return errors.New("短信发送失败") + } + + if response.Body.Code != nil && *response.Body.Code != "OK" { + if response.Body.Message != nil { + return errors.New(*response.Body.Message) + } + } + + // 检测唯一值返回 + if response.Body.RequestId == nil { + if response.Body.Message != nil { + return errors.New(*response.Body.Message) + } + } + + return nil + }() + + if tryErr != nil { + var sdkError = &tea.SDKError{} + if t, ok := tryErr.(*tea.SDKError); ok { + sdkError = t + } else { + sdkError.Message = tea.String(tryErr.Error()) + } + // 如有需要,请打印 error + _, err = util.AssertAsString(sdkError.Message) + if err != nil { + return err + } + } + return err +} diff --git a/extend/aliyun/oss.go b/extend/aliyun/oss.go new file mode 100644 index 0000000..9272927 --- /dev/null +++ b/extend/aliyun/oss.go @@ -0,0 +1,186 @@ +package aliyun + +import ( + "bytes" + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/aliyun/aliyun-oss-go-sdk/oss" + "hepa-calc-admin-api/config" + "io" + "os" + "strings" + "time" +) + +// GetOssSignResponse 获取oss签名返回值 +type GetOssSignResponse struct { + AccessId string `json:"access_id"` // 主键id + Host string `json:"host"` + Policy string `json:"policy"` + Signature string `json:"signature"` + Expire int64 `json:"expire"` + Callback string `json:"callback"` + Dir string `json:"dir"` +} + +// GetOssSign 获取oss签名 +func GetOssSign(dir string) (*GetOssSignResponse, error) { + now := time.Now() + expire := 30 // 设置该policy超时时间是30s,即这个policy过了这个有效时间,将不能访问。 + end := now.Add(time.Second * time.Duration(expire)) + expiration := strings.Replace(end.Format("2006-01-02T15:04:05.000Z"), "+00:00", ".000Z", 1) + + start := []interface{}{"starts-with", "$key", dir} + conditions := [][]interface{}{start} + + arr := map[string]interface{}{ + "expiration": expiration, + "conditions": conditions, + } + policy, _ := json.Marshal(arr) + base64Policy := base64.StdEncoding.EncodeToString(policy) + stringToSign := base64Policy + h := hmac.New(sha1.New, []byte(config.C.Oss.OssAccessKeySecret)) + h.Write([]byte(stringToSign)) + signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) + + response := &GetOssSignResponse{ + AccessId: config.C.Oss.OssAccessKey, + Host: config.C.Oss.OssCustomDomainName, + Policy: base64Policy, + Signature: signature, + Expire: end.Unix(), + Callback: "", + Dir: dir, + } + + return response, nil +} + +// CreateClient 创建客户端 +func CreateClient() (*oss.Client, error) { + // 创建OSSClient实例。 + client, err := oss.New(config.C.Oss.OssEndpoint, config.C.Oss.OssAccessKey, config.C.Oss.OssAccessKeySecret) + if err != nil { + return nil, err + } + + return client, nil +} + +// GetCusTomObjectToRAM 下载自定义风格文件到内存 +func GetCusTomObjectToRAM(filename string, style string) (string, error) { + if style == "" { + style = "image/resize" + } + + ossClient, err := CreateClient() + if err != nil { + return "", err + } + + // yourBucketName填写存储空间名称。 + bucket, err := ossClient.Bucket(config.C.Oss.OssBucket) + if err != nil { + return "", err + } + + // 下载文件到流。 + body, err := bucket.GetObject(filename, oss.Process(style)) + if err != nil { + return "", err + } + + // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。 + defer func(body io.ReadCloser) { + _ = body.Close() + }(body) + + data, err := io.ReadAll(body) + if err != nil { + fmt.Println("Error:", err) + os.Exit(-1) + } + + return string(data), nil +} + +// GetObjectToRAM 下载文件到内存 +func GetObjectToRAM(filename string) (string, error) { + ossClient, err := CreateClient() + if err != nil { + return "", err + } + + // yourBucketName填写存储空间名称。 + bucket, err := ossClient.Bucket(config.C.Oss.OssBucket) + if err != nil { + return "", err + } + + // 下载文件到流。 + body, err := bucket.GetObject(filename) + if err != nil { + return "", err + } + + // 数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作。 + defer func(body io.ReadCloser) { + _ = body.Close() + }(body) + + data, err := io.ReadAll(body) + if err != nil { + fmt.Println("Error:", err) + os.Exit(-1) + } + + return string(data), nil +} + +// GetObjectToLocal 下载文件到本地 +func GetObjectToLocal(filename, local string) (bool, error) { + ossClient, err := CreateClient() + if err != nil { + return false, err + } + + // yourBucketName填写存储空间名称。 + bucket, err := ossClient.Bucket(config.C.Oss.OssBucket) + if err != nil { + return false, err + } + + // 下载文件到本地文件,并保存到指定的本地路径中。如果指定的本地文件存在会覆盖,不存在则新建。 + // 如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。 + // 依次填写Object完整路径(例如exampledir/exampleobject.txt)和本地文件的完整路径(例如D:\\localpath\\examplefile.txt)。Object完整路径中不能包含Bucket名称。 + err = bucket.GetObjectToFile(filename, local) + if err != nil { + return false, err + } + + return true, nil +} + +// PutObjectByte 上传文件 +func PutObjectByte(filename string, content []byte) (bool, error) { + ossClient, err := CreateClient() + if err != nil { + return false, err + } + + // yourBucketName填写存储空间名称。 + bucket, err := ossClient.Bucket(config.C.Oss.OssBucket) + if err != nil { + return false, err + } + + err = bucket.PutObject(filename, bytes.NewReader(content)) + if err != nil { + return false, err + } + return true, nil +} diff --git a/extend/rabbitMq/rabbitMq.go b/extend/rabbitMq/rabbitMq.go new file mode 100644 index 0000000..606a1c8 --- /dev/null +++ b/extend/rabbitMq/rabbitMq.go @@ -0,0 +1,284 @@ +package rabbitMq + +import ( + "encoding/json" + "fmt" + amqp "github.com/rabbitmq/amqp091-go" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/global" + "hepa-calc-admin-api/utils" + "log" + "net/url" + "sync" + "time" +) + +// Client 客户端 +type Client struct { + m *sync.Mutex + queueName string + logger *log.Logger + connection *amqp.Connection + channel *amqp.Channel + done chan bool + notifyConnClose chan *amqp.Error + notifyChanClose chan *amqp.Error + notifyConfirm chan amqp.Confirmation + isReady bool +} + +// ConsumeS 消费端结构体 +type ConsumeS struct { + QueueName string // 队列名称 + ExchangeName string // 交换机名称 + RoutingKey string // 路由键 + Handler func(amqp.Delivery) // 回调处理方法 +} + +// PublishS 生产端结构体 +type PublishS struct { + QueueName string // 队列名称 + ExchangeName string // 交换机名称 + RoutingKey string // 路由键 + Message interface{} // 消息内容 + Delay time.Duration // 延迟时间 +} + +// NewRabbitMQClient 初始化客户端 +func NewRabbitMQClient() error { + var err error + m := config.C.Amqp + + user := url.QueryEscape(m.User) + password := url.QueryEscape(m.Password) + + dsn := fmt.Sprintf("amqp://%s:%s@%s:%d/%s", user, password, m.Host, m.Port, m.Vhost) + + // 连接到 RabbitMQ 服务器 + global.RabbitConn, err = amqp.Dial(dsn) + if err != nil { + return err + } + + // 创建一个通道 + global.RabbitChannel, err = global.RabbitConn.Channel() + if err != nil { + return err + } + + return nil +} + +// Publish 生产 +func Publish(queueName, exchangeName, routingKey string, message interface{}) error { + err := global.RabbitChannel.ExchangeDeclare( + exchangeName, // name + "x-delayed-message", // type + true, // durable + false, // auto-deleted + false, // internal + false, // 阻塞处理 + amqp.Table{"x-delayed-type": "direct"}, // arguments + ) + if err != nil { + return err + } + + _, err = global.RabbitChannel.QueueDeclare( + queueName, // 队列名字 + true, // 消息是否持久化 + false, // 不使用的时候删除队列 + false, // 是否排他 + false, // 是否阻塞 + nil, // arguments + ) + if err != nil { + return err + } + + // 将队列绑定到延迟交换器 + err = global.RabbitChannel.QueueBind( + queueName, // 队列名称 + routingKey, // 路由键 + exchangeName, // 交换器名称 + false, + nil, + ) + if err != nil { + return err + } + + body, err := json.Marshal(message) + if err != nil { + return err + } + + err = global.RabbitChannel.Publish( + exchangeName, // exchange(交换机名字) + routingKey, // + false, // 是否为无法路由的消息进行返回处理 + false, // 是否对路由到无消费者队列的消息进行返回处理 RabbitMQ 3.0 废弃 + amqp.Publishing{ + ContentType: "text/plain", + Body: body, // 消息内容 + }) + if err != nil { + return err + } + + return nil +} + +// PublishWithDelay 生产延迟消息 +func (p PublishS) PublishWithDelay() error { + err := global.RabbitChannel.ExchangeDeclare( + p.ExchangeName, // name + "x-delayed-message", // type + true, // durable + false, // auto-deleted + false, // internal + false, // 阻塞处理 + amqp.Table{"x-delayed-type": "direct"}, // arguments + ) + if err != nil { + return err + } + + _, err = global.RabbitChannel.QueueDeclare( + p.QueueName, // 队列名字 + true, // 消息是否持久化 + false, // 不使用的时候删除队列 + false, // 是否排他 + false, // 是否阻塞 + nil, // arguments + ) + if err != nil { + return err + } + + // 将队列绑定到延迟交换器 + err = global.RabbitChannel.QueueBind( + p.QueueName, // 队列名称 + p.RoutingKey, // 路由键 + p.ExchangeName, // 交换器名称 + false, + nil, + ) + if err != nil { + return err + } + + body, err := json.Marshal(p.Message) + if err != nil { + return err + } + + err = global.RabbitChannel.Publish( + p.ExchangeName, // exchange + p.RoutingKey, // routing key + false, // 是否为无法路由的消息进行返回处理 + false, // 是否对路由到无消费者队列的消息进行返回处理 RabbitMQ 3.0 废弃 + amqp.Publishing{ + ContentType: "text/plain", + Body: body, + Headers: amqp.Table{"x-delay": int32(p.Delay / time.Millisecond)}, + }) + if err != nil { + return err + } + + return nil +} + +// Consume 消费 +func (s ConsumeS) Consume() error { + err := global.RabbitChannel.ExchangeDeclare( + s.ExchangeName, // name + "x-delayed-message", // type + true, // durable + false, // auto-deleted + false, // internal + false, // 阻塞处理 + amqp.Table{"x-delayed-type": "direct"}, // arguments + ) + if err != nil { + return err + } + + queue, err := global.RabbitChannel.QueueDeclare( + s.QueueName, // 队列名称 + true, // 是否持久化 + false, // 是否自动删除队列 + false, // 排他 + false, // 阻塞处理 + nil, // arguments + ) + if err != nil { + return err + } + + // 将队列绑定到延迟交换器 + err = global.RabbitChannel.QueueBind( + s.QueueName, // 队列名称 + s.RoutingKey, // 路由键 + s.ExchangeName, // 交换器名称 + false, + nil, + ) + if err != nil { + return err + } + + msgs, err := global.RabbitChannel.Consume( + queue.Name, // queue + "", // 消费者标签 + false, // 是否自动确认 + false, // 是否独占 + false, // 是否无等待 + false, //其他参数 + nil, // 其他参数 + ) + if err != nil { + return err + } + + // 消费消息 + go func() { + for msg := range msgs { + s.Handler(msg) + } + }() + + return nil +} + +// Close 关闭rabbitMq +func Close() { + if global.RabbitChannel != nil { + err := global.RabbitChannel.Close() + if err != nil { + utils.LogJsonErr("关闭rabbitMq:", err.Error()) + return + } + } + if global.RabbitConn != nil { + err := global.RabbitConn.Close() + if err != nil { + utils.LogJsonErr("关闭rabbitMq:", err.Error()) + return + } + } +} + +// HandleReconnect 处理重新连接 +func HandleReconnect() { + for { + notifyClose := make(chan *amqp.Error) + global.RabbitConn.NotifyClose(notifyClose) + err := <-notifyClose + if err != nil { + time.Sleep(30 * time.Second) + _ = NewRabbitMQClient() + } + } +} diff --git a/extend/weChat/base.go b/extend/weChat/base.go new file mode 100644 index 0000000..242318a --- /dev/null +++ b/extend/weChat/base.go @@ -0,0 +1,105 @@ +package weChat + +import ( + "context" + "errors" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/core/option" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments" + "github.com/wechatpay-apiv3/wechatpay-go/utils" + "hepa-calc-admin-api/config" + "time" +) + +// 创建客户端 +func createClient() (*core.Client, error) { + mchId := config.C.Wechat.Pay1281030301.MchId // 商户号 + mchCertificateSerialNumber := config.C.Wechat.Pay1281030301.MchCertificateSerialNumber // 商户证书序列号 + v3ApiSecret := config.C.Wechat.Pay1281030301.V3ApiSecret // 商户APIv3密钥 + privateKeyPath := config.C.Wechat.Pay1281030301.PrivateKey // 商户私钥文件地址 + + if mchId == "" { + return nil, errors.New("商户号错误") + } + + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath) + if err != nil { + return nil, errors.New("微信支付生成失败") + } + + ctx := context.Background() + // 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力 + opts := []core.ClientOption{ + option.WithWechatPayAutoAuthCipher(mchId, mchCertificateSerialNumber, mchPrivateKey, v3ApiSecret), + } + + client, err := core.NewClient(ctx, opts...) + if err != nil { + return nil, errors.New(err.Error()) + } + + return client, nil +} + +type WxPayResult struct { + OrderStatus *int `json:"order_status"` // 订单状态(1:待支付 2:已完成 3:已取消) + PayStatus *int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PayTime *time.Time `json:"pay_time"` // 支付时间 +} + +// HandlePayStatus 处理支付状态 +func HandlePayStatus(t *payments.Transaction) (w *WxPayResult, err error) { + // 支付成功 + if *t.TradeState == "SUCCESS" { + orderStatus := 2 + w.OrderStatus = &orderStatus + + payStatus := 2 + w.PayStatus = &payStatus + + parse, err := time.Parse("2006-01-02T15:04:05+07:00", *t.SuccessTime) + if err != nil { + return nil, errors.New("支付时间处理错误") + } + + w.PayTime = &parse + if err != nil { + return nil, errors.New("支付时间错误") + } + } + + switch *t.TradeState { + case "SUCCESS": // 支付成功 + orderStatus := 2 + w.OrderStatus = &orderStatus + + payStatus := 2 + w.PayStatus = &payStatus + + parse, err := time.Parse("2006-01-02T15:04:05+07:00", *t.SuccessTime) + if err != nil { + return nil, errors.New("支付时间处理错误") + } + + w.PayTime = &parse + if err != nil { + return nil, errors.New("支付时间错误") + } + case "CLOSED": // 已关闭 + payStatus := 6 + w.PayStatus = &payStatus + case "REVOKED": // 已撤销(付款码支付) + payStatus := 7 + w.PayStatus = &payStatus + case "USERPAYING": // 用户支付中(付款码支付) + payStatus := 3 + w.PayStatus = &payStatus + case "PAYERROR": // 支付失败(其他原因,如银行返回失败) + payStatus := 4 + w.PayStatus = &payStatus + default: + return nil, errors.New("未知支付状态") + } + + return w, nil +} diff --git a/extend/weChat/certs/1281030301/apiclient_key.pem b/extend/weChat/certs/1281030301/apiclient_key.pem new file mode 100644 index 0000000..35a62ce --- /dev/null +++ b/extend/weChat/certs/1281030301/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDR4fiYRUjnLQA+ +TPMslDvQIIyZm4ajWFcSXg6yozhdi/O+vuRhwgyeGXsE4SpvuxkK4HPZaocrsjqe +68Y46DyhudzXIBy50IF2UeN5ilj7ydq5TJbBOi1iOF1lgOUNK6DGiOQ+folVNKyN +sjrsA3RHUKHF/JdOfcpSGzvV79FnmjSll/KsdxEc9LCiAa5t7GYA9Hx7AIDG4i8b +03d5EJXvE8BGUZQItE6VIpHwyySTHtGQkPyI6lqMKt2Jlx0TFHu0GkTEmhuA2Z5C +5gOqYWclQO+X2ORgulL9Q3mNcpfy9uBynI/CJDbSvVTkNfwYB0Ny1knu/+in7C6p +IPJYfZpPAgMBAAECggEAWb5tJPcjSC5W10ziAiLUPJdeZ2Q4OupQOPtc/4eJV367 +V8maMC7gZE3y61A4bBQtjhgRkVrat5V7OW8JkFXFb0XhJ1+EyPNeGDDFureseuWC +EA+uuqrcsw306a0mw+3uzlXEevByWquuSNx4E2katE/HDLiIHjjtZRReDomAGfLw +vLNZ40RgdNSXmwpVRoHOZnvX+C2Hxh4fKsrxs1HRCjFsSpTxKwsK6/Kdudj4hsaO +Z5glqf4qopPlQIIkToxOe5p7ukwo7vqaqogixx3jOjruOMpzxZ24wU5AxneLcOYr +ZC1659Uwv96TsKpueakWw0lq3TXkv9Qs+wDNI+NC6QKBgQDx4hWXplLYPYwgGgxG +L9Pk+If9aDS9wj78JUaYBXKR5atXmf/62ufahW4IEDfmMugC1bm9MoYC7CmHv1nk +nxckB84B20NOSiaxbkOaJuMHDtnNeXCmL8Gvtb8fTni1AM/g6XEleEw1r40LNZAq +kdyQ1+OUFhRQGOvAe1SRr1rdGwKBgQDeIcauwMPkc3Seh6Fb66FQ4Lgmt1GvvuiG +UJiwgwPFbEaKKczBHBL6hlKoAFweFa2Qw2xJ0H8jwb8RnkkNcAKYUyMc2wi85+Ih +d568pvXMdYl4DGeGdd/sXKf4dGDjcb2AHzNfDuklbCsAojtO3pLWOs5U/RSKe8ps +hhWa8nPO3QKBgQCIxuKU3X1tP+hz4qbcLYFxscQcXIeuYiABrwZrQnFV5Pxtzex9 +Krn+zIK61oj1iAXATKD6Ro6XKnoVg/POHtQUEMHCNP2rUKzumj5p9eFdBV3OHgTA +RLMOrARGLLZ/C9WBBiBwIsVdekaUdxZtrAuAcEQFYjLcVCtDrbnVo8YKzwKBgEsg +V0cBMP+RwM5hBsTE45Er/3wwofLzeUb7+Tgxh1P887qEuphRO2X5ifkB7iXKpSIB +xh0M5AMe4tU9mG1wBaCo9YYr2j+xmTxCbbBWM2mMEwtD/rtuIGabS7/u9FnYPQQZ +CVHMBDRA6iZTuAVLp5PG3cPGuGzBw0uC6cm22E4NAoGAKrbg3nFPKtzBJZ8Z0iNH +G2gNotmHXbd4+gs4e3Iz9xsEabm4wCoEibNojNdMkX8zX267ebgfvesGEobROjW9 +Intvh39xi3aQ2Q5gvXzKdY0lDzgVvQ7udmLQ4dCUyYnpLr7Yac0asRxLge2esCWV +x2uW2YTiUW3zsbHN/N/vJ5I= +-----END PRIVATE KEY----- diff --git a/extend/weChat/certs/1636644248/apiclient_cert.p12 b/extend/weChat/certs/1636644248/apiclient_cert.p12 new file mode 100644 index 0000000000000000000000000000000000000000..b5df8dd0b102f01e9cd6f8ad3eccced2874cdfb1 GIT binary patch literal 2774 zcmY+^c{~%28wc=hHpdFZ#9Yl0Ibw`NNUnsObL5z-ZOEK+k`-i6Ya!Y~P`VrKVT1(4>pWN0;LuEN z!zW!uG8~-+mm}LT1e|8d%*+oTjUeO5l$&beu;{g|9dv&N-t9B~bq-f6njz5JNnhQy zwQ?t}iW4)n8k2T^;K!iOa$kqDBTV2|vjp4Ed%wFqQ46))&@NYhl}i}DqjLcXHLJwC z{z9G5@KCQFiIabE;)+fI`pQpl`FwPaE4hT3QGs16?R6KguRXYGqbA|-CCa(+?ab|x zHq)eUxXHd%cDRiRIn5w9x?lJcvZNk~+%^$fi~nN$Dn$z9|~;A>ctg zqCdSld_GFyuzkjoyMRe%6YspM#-aBTF2z?2`QKVoIooA@_)~K3IxDpDsh?Tm`N!-A zvmC4TLL+2$enbhXx&Zc}fc{~zz-R-le-LrW%`%SR!`~35NwMmHI-$y^?Z8 z2G2RYl&Sma4N*oKweN-+HWgK;gdSZvHlF8+bS^2R;dOAb-!~%Pz27|esPM{hJmpFA zLxXo^E{9w0{El7`^0ooG9ifpK%eWrT#B%@9oVw!=6v94Ah{chLD?2y=UbDj?B@?S8 zwFBYbV_&l|f(Frh%3(Hx-R;wN%B9CAjsc~-3HIfSLB#!fhlRS-5Pu6L=Hx;=E4!t@ zT-C0+^ou>4?spC7adY?pXzwZReGWcCXyw;_1602a+Ix>|%)2^Yh8}j_=ad*jy;PG( z?*<{e>;KlTFozQ@STbgY!rP0!O{`_F`h9NLgXk@sV zD7YfOu3^Ue7?9a+6F~g5H1GQIqF*exaLwJ!Ep@)@eK~<<;u2j}Jk6$ba16#$$#kLC zlo=?=zE4#(mZ{^>PvMCfS`hV$zc-HruWR%7lVj)EI3vM@8|X}qAW>CUc-`Io=~p{EF} zCg(c1;AFQP8*yb|*#ND`FUcweaejbu;nyBnP}%vWCiUh_TGoVozUpyH?;TiMwEC0k znnzJe9`qhw1(7xP#<6BpD&!mYCgvq9ZJR+b&jD^p@psR4ZG#RPL+l( z-VZl)APxGJ60YN}n5e?z7=Am_;V#>ar9plPy6ZIahO64r@Yv8&UoJwD-kvERGvk)u zB@1+IwR-`VAUik1T`f~(346YgV1wJKXqXSpmNUrp~#I)v3*A!KoCT(zhDyWNKoJ8Y4ZWf@;{R=NsG(vcExw-r$@^ru`J zCZm`d;yG>)r=&k*kx3w_))~G2#3=JTqA=)xz2s(0d z?Fh)_|5K6a9~G-fRY?I;_}Bla2quHOf_*4XC7_Pv?;@nvu;X9e?j4?+ufcHNNPHj< z$LdY5xX_E;Br1a%qdk?<<;N>rNt}JovQYt+`Q{6iwV_#6W`e{f$W|bTD^c&FLsfQ` zK;G8ZwfAd3eyKRyKA0cDwB$|CiVcPKm}}p)P2k3ICb1gtF;FI3i%ar1It_#i{Pe3w z@Fxc`kQen8L7o>6y2lKzvC0j4b<9O%Fz-?!S$$ z>-v#kskAOLKzpk!SCY<%oz|ka3SF>dW9!&dhQ1VTa|W#?{nC@DhKsXM5xzsI*UAga z_IGU+w&q4~!5SlYr4XrC(xP8WX)(^#xpgYS^ZBv4qpHk`Q9(g&wEpI%?JUmQ+f0D@ zn`g^>mr*c7?kWrmN)3yY$2YeC@t?yf{*V$ zNBto3X4!G?6^0aczcqXpP%2bYq8d2=m`UHkTBOpT;=Y{+k8xFR z!E^H*$hJp`eX!Adk9x-A!-h{)_dU7Th@b^5f=aQV$NAah*4om3$7y47?i&7pRc{WM9u@~-$D+_nLAOuZ$`_mR=v z(S{--J+=z++&%pXs1BxA066n&z4+}XD}8bUd!bd z>n;&z{IW8_@=1i3wQT0EYq|Q-vwgTnHvxAEvwEzJGd0Vr)w--AZY@#gxCc36V1%yH zeJ59t2`VJb(Pqc4Acm5=)IHf{nC2J31eo&~?ziB5%q~TV*rJ<<@2XS1b#^KyCBK~c zYHzQ(!rXG|K?`WOBIv3S%d3(b$-KoLQ>XTFUamAw8WFIVjQVh;g}K#Q4N0FDFZ(x# zORPOGed{vqFk^MPsKkItQ4B?K$|0^b``X*$F-Gfo>OsQ!WvFg1(imiiu(kSm0lbO3 zx7cC7EVGf|595qj1Q%;XmVe*H%w|J&ZSWGDx}#PIF1z0c_a5GSW1V8sEDO_@jcM7N zriY4s+kJH!dVryG`;I4?`>G~{b6iMFPQ!|JFz6*nV&&~{KcOz+Zn&M)9Xf?A&`!Y) zDfV9-*?1IAfnh(Q%iQ;jeH%r6Op}+SPnZdqOJyTbe~(}a62jsDGyn_m2KWM80HFW` zK<8-k2M_@s0Nhcz9i>D77I_XSf@EWc$Z#-$_#pr=ERBC82n2me;Bj8l#$b{ P2WC~6nh_J`-!%Uh$j=r| literal 0 HcmV?d00001 diff --git a/extend/weChat/certs/1636644248/apiclient_cert.pem b/extend/weChat/certs/1636644248/apiclient_cert.pem new file mode 100644 index 0000000..101b476 --- /dev/null +++ b/extend/weChat/certs/1636644248/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIELjCCAxagAwIBAgIUfewObFfg3HHwd/AvUkBlZq85vrswDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjMwMzAxMDExNzE3WhcNMjgwMjI4MDExNzE3WjCBhzETMBEGA1UEAwwK +MTYzNjY0NDI0ODEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL +DCrljJfkuqzmrKPmrKPnm7jnhaflgaXlurfnp5HmioDmnInpmZDlhazlj7gxCzAJ +BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAK7x9ywApasrs+xIt0XxHhYOK4xZeuNEj5o7pzTcZMRkd6CB +1eX2ydlUfDe+FcNS03cwLNtUeAfrZXlhA807b4HuReaFqCrbt87hfIF/lOBpePdN +sSr8Wi6OKfPakuLNZ4RCOdlvgxPMhOf2b9VFQwns8h72H6JrFz7xR+Heundy3KGH +kEoG2qcl7nKyhkUVSRSH4/yzsxsDIwkpXjEiSL87E+A6GKik7jphYc+vfV9NURiA +JwbVWbQMhpj3YLRxgXadLS4xMryB59fYKm+VFMNg/jSZG55Jz2DW02aR2h3KjegY +SGA9qMKojkFVOrZvxKWOvtEWw2JQ/1dRvzYpgw0CAwEAAaOBuTCBtjAJBgNVHRME +AjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDov +L2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUw +REJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJF +MTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IB +AQCBjjBeJTRtnsIISCfoQ7pj70dil5LTBNjpzV9swGOG7sY9dTMbHV0K2BUyRvbD +eY2fuCcr3Qt3L1RykyomKbxD4O6b/ZPLTkiq2m+hq34g2Ig7zUx0a0sNKf/NPAq6 +6pVu5/XdIKYXz2OsByBI8aN2hkcUApmM1qlm+gb4FSmTzkdHaFRblhDlKfiRdAw8 +T4yliaOFovqhf/S8o5Xa76kQMOb3uJ/oE4KX02kp/ig5wZKj9nay46AyovBXOj+Z +0oaeIPFDXCPzZV7LA4E45zDl43fL8r+9OzaVprRZ9ASO0cgnPsHyS+o/mpMEBOR4 +NIwdIaqYRQ/Rh6eZQvrqDZ4r +-----END CERTIFICATE----- diff --git a/extend/weChat/certs/1636644248/apiclient_key.pem b/extend/weChat/certs/1636644248/apiclient_key.pem new file mode 100644 index 0000000..8e9da3d --- /dev/null +++ b/extend/weChat/certs/1636644248/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCu8fcsAKWrK7Ps +SLdF8R4WDiuMWXrjRI+aO6c03GTEZHeggdXl9snZVHw3vhXDUtN3MCzbVHgH62V5 +YQPNO2+B7kXmhagq27fO4XyBf5TgaXj3TbEq/Foujinz2pLizWeEQjnZb4MTzITn +9m/VRUMJ7PIe9h+iaxc+8Ufh3rp3ctyhh5BKBtqnJe5ysoZFFUkUh+P8s7MbAyMJ +KV4xIki/OxPgOhiopO46YWHPr31fTVEYgCcG1Vm0DIaY92C0cYF2nS0uMTK8gefX +2CpvlRTDYP40mRueSc9g1tNmkdodyo3oGEhgPajCqI5BVTq2b8Sljr7RFsNiUP9X +Ub82KYMNAgMBAAECggEAT7uNyGs/FkVjykPV67WZ3bl1lZDOljgQLt4TNd9gubWE +ZA3om9efZULBHnKu3oeoQ0EcoJXd4tYhOHHD1szI5HHhP9AYtffPzSUtpqOsCZ9o +d2XcYlgDDgbTDgXHPkEZdcjtLrFJD0P+Ku5BR/U6OZLZQs0v28ltHc2/0iy91WQt +iB/NSf88dY1L5RVZc7dZfb8nFB2/BsITUXDFJpzdim3AXKvNYzDajs/yuoTp5r/b +2EOjUIGPP55W/8iPwbezgDToF79toum6V7yUpCq5P+QQvnnbJfoc1yXB48zHjmgP +I2P1cDVtFs+V9Edb6qsU3e4CMyBQg5Uykk3TUqZRIQKBgQDnXgGk8XnI1kG9jZMR +wMkra87Dx76vrBdM47n+c2I1xubM9WSeSUxtXGyryTwJ9i6/BUyc3Eyb2wi4Q8AK +1ZIEFhvIUkBJpUgevAksj4eX6z0qDwDc3ZhgAAWOplnOCCyJuOBKmZks4GaQb4Gv +/aSFVQ9jefOc5e9RIDNzkHFkaQKBgQDBkigvmusjad95Jt34TwyE4Dr9z2LzBker +6ebcyQmv4YdKvq0KaaVMrroH7pNu7CSFrj4CGqdS/Gg1BtK8Xaxn+gEWT6RPcsi1 +mPwy/7oiK0GodzNI6RW5HDZEHKG2icEb4ycDnHeLfThKmP/gckPifjcJHrP5OX1A +V6qrq4iFBQKBgGdgB1gNVJ65rJHnCckq3Dd8advsCXUwbRC7x0S7hSwF/OWi1xwq +H+3VF/EBbsP8rRJIadzESa5xhUnfa5Trq9wLjMpKhdLh+IFS/r5cOvdT8fYy0e3d +TNHH8LO1+/YkjNHUOtLaIih88xah284ohDPWt5N4z7JQwkb7HkIKTb/RAoGARt51 +7Afx8sM+WCLMva5jTPqzXl1hQsyXzO8T4N2RuFz/pXPt8pP/OvX1khXc0I2QSYkj +lq2feRiEJnXbDa/WATNc1ohOBfBmX2YlX56UzRG9Nip+EkGT/HPBwmohIq2Ij+c4 +T3AnrGAqDdW6SLhM9k1zZNli1uofW0E9cSCaGOkCgYAI29eelAwUxpbDXJB6vSyr +LBvDV+C+/3OW4AjlJ5lWSzY8oMn21Xzp03MwXOXOSFuR6vTMkJDKJ3zIDHBt9vJ2 +evNmfMKzqNdsvNjORb79GAZ0paBF1XGzXvPO9JSUi6oZxNg+pW8oxIzx0xFIgtZL +PxnzFj2IbraxYwuR1CI6rQ== +-----END PRIVATE KEY----- diff --git a/extend/weChat/certs/1636644248/wechatpay_112FCCD1B9ECC8292703AB7363C73D74B6AFDC1A.pem b/extend/weChat/certs/1636644248/wechatpay_112FCCD1B9ECC8292703AB7363C73D74B6AFDC1A.pem new file mode 100644 index 0000000..ab18f68 --- /dev/null +++ b/extend/weChat/certs/1636644248/wechatpay_112FCCD1B9ECC8292703AB7363C73D74B6AFDC1A.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUES/M0bnsyCknA6tzY8c9dLav3BowDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjMwMzAxMDExNzE2WhcNMjgwMjI4MDExNzE2WjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvM2YfnzwBvd+B5Ro1 +z+LaCVYhxia9/hQwRhi5ag2HmeZZNgMNf4+bC75Nv+t3RW0lklbM9qJSBk03eEBG +75Q33VPGy//WCJZgXlHV+rZ7ejzWtwh9Muhd60HFXhIsM3pwlfWpPo6bkJie9Na0 +wMsgg/8UxsNydhEZF6HFLdqY+zqGOjRRCduIyJcFhtrkjNUMIFAOSkHBaGJjmGrm +OXigAnYAsaD1VWLOoblA0HlOs144KQ/5Shj74Ggk2pFo/YDN+i5hazHZo9hZnjmX +G5BV3KDJD0k3Gn/qymhiLlzGsYW79P+BbsmE/M6jKIP2jxcDDdpek0Z6Lk0Cz/au +02UPAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB +kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv +aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 +RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 +MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAJX6C/QMYF0F3IiK9P8tW2DKN8y9vCU20 +6Ws8u4bcO3AaiSmsdAJ6I1MyZkUg3dKijtnDbieY2P364IEp48TmI4k6UJwP4+/f +i0NseOm3BmAJ3mBoNmuFul+61opKpeV67AYQuhbehVA4cDPqKb/hP0tEcb6nefIO +mnys5GReLWLFV2XFR1h9QtsohPOEYlpBl6lmKHNoQZdAPtSHq2iVFLbuD7GMKoj7 +Br2puJEQgxla1AsNxAYjGttEIO9I30+L5av7SKesSGXvL6G5eOvFrZl0S4EsNVz4 +QeL4qmtFEKDi2eaxeyLuWe9TMI/IiI3ngekoDyTwdnUzadumbio+dg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/extend/weChat/certs/1659662936/apiclient_cert.p12 b/extend/weChat/certs/1659662936/apiclient_cert.p12 new file mode 100644 index 0000000000000000000000000000000000000000..13982955238fc8f1eecf1a1a05c1c6afd92b4080 GIT binary patch literal 2790 zcmY+^XE+;-76FSBKeBRn$y_6sb|OcFa;#6|pL2(N;sm zC^f2ft7^9PUf26P_ulv359h=A|Ic$i{t$TR4-k+Bfrpku7-S;#BX^mBbijN(G#8AA zW}d}q2s|y%KNTb&PfLFmfk8k30uMg^rvV}aA<%z+U;;uAEMWR!dnV_^T*qS&h#nA% z2lrnnv+wOu7StlCJ^E&_x%ku0Rbsx<`Td)4OeQBT_=(FisIgA1u-cjo zUJFd8SMB50A5tk)!(y3WA{2-p-6hp7Y5nRjoH6K;3s|{5yK()A>~tML8K^7=@p&-S z%OR%ZGI<+nqA*ZWhDxbWC>s{EAzo191tK2+qv`I7G+b$IM|XZ$n$o$lz*q_iyDuz4 z5%$s1UA;@zQn{8l_g1JvOt@4n_E1p5J4;fGLMv-sO#g+c66J>@1`{PC%(uQY5b}$#=BrHFwlZRe&WAuV-Nx!}D80U8}D{ zrcK00)2?Dup%LHHDCD)_n!;$kRY{Z5EAo}wdF5I%t35UBaao=RfX$ zY~X9ix%uoyZ(NZb|FTw}(l*XUq<&2+_fr1X9OjwKn$=0lI$kJ9)IB?Xo${-Ife2k9p z-ThM9KgTbm9fe%x5Jh+Na+`am04MbvnNLc;0k#duA{Y*DC|BH!o?oIAoO^I`68plmh25o$9Vjd;ErkO8b(Gdut(SUlYa3`}#`C3bMe0WWjqPTO$IwEU5HQ)js}b;W zhgHLYQBqy|U>IZJrPIbUQP(?!5EoqwKj=fESi z%(`9!^fK&UYW+vgx!GQk(@Qg-RFrL==EVe(J zTM)+oa0ms0^6}t5XJ9V)fQn3avU^ier_G|gBBc}0{I4KkPW`d!G239fq@%+uwgeQ5zZTTA zbBn>AbZXc}u`SB{9f+^vsKbFdy?)=j5+j1L&6&6{zoz|E;(<(%305lWTtM14YXl+W z`4NoWOcm2=de90Ny*1@pymU}$r=KKw!t?n`a|CR;I)7s{tXXX6bkSFEkSagWLNAKi zf(+b^+|nd&a_i|V%J`U0O1}umh-3Gj?fFYRgU2a=rdlkT!dOiZ_md47i`l`(&)OI5 zR-NOu>3MCWsk*i_p5QxlF0bh259D2`ckGI>YGK<`3O{l9_$lqIgFPQufK#Cc`1 z^gR&UyvNTt;{w}7ZZC;!<_C>+A*Q-+(!b{}><2{&uoh#00%`4=w02`jnnY=v`te)) ziyj!m)Ta9Uiap*Dbmz2rM&eFkn=RXX9bLmACEIhYd3Hfq=ys4>YMNcjAYF)M<~mFFlD>K9ol!mycBs1YbT`s8ab06FeQ4P$PPUX6hV3QoxSgfT#~7hb~qUy}IxA4@Az;o1nT7#L4?$_IOxL*m)FyWN7L7);!e}dkmD})&YGo+S(CP zK8{;Zu4qIw)GsKLhTPs69N6&xE?_7oV{Wmb!md{n7A}umm;W)t#t)jhn>bKo=K<{b zgwL)1i)=d3c^#z6{DkD?%lH+Wcb&hQSc}}(OOC3pnGDw}kQC0gb_i!)rFtyu`fbg& z$e&(-mF30+Tw8(LWucjv4lA7IX}C@2I0Vr1+LQO1!c!V&S*`M1xnkA^+uB^Wyfuc& zJ2fIQaj_?Tc*Z#dz~>=v0)f)b2HlIOmpCqXBu?#!ZkzFlZkF|g6cjr-`*4-|@l}#E zSkNT|X6#BY@_t&q#Er}vmk6mPX%P2uBBDnB11d;F}X{EDOMh7HGEn6+6d z?2Z9?f>0%U$>7`TIlc@hj#=9h)5e;v`vd*r{uO1P^=N80t9s>j-b_?S;6Bg?!(dhD zh31cu$gLy6S0^o6#Eo2#1|u~z&a(H>qDdzc;yGY3YGaA?l(CF$((1++L#`HkyM2s% z+v%;m^_C@nvkHI3n+Wm$>d3&Q$PMO9bv0)YC`--YNS-y?(=fcBUTKU%ApbeyRFo8g z0W1Oc0G@zH07n28AOg@n+k60i05^ck**Kr2et>%jIfN*J0YVFBp#kyJ0>GTTOE6x( d3+mH*Q#H#3Kkw8C*z{{K{nSjRVjzKP{4bBADd_+J literal 0 HcmV?d00001 diff --git a/extend/weChat/certs/1659662936/apiclient_cert.pem b/extend/weChat/certs/1659662936/apiclient_cert.pem new file mode 100644 index 0000000..514b2af --- /dev/null +++ b/extend/weChat/certs/1659662936/apiclient_cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIUEvql9hcIt5W7UzeukVSU4twsqHswDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjMxMjE5MDUxNTA4WhcNMjgxMjE3MDUxNTA4WjCBkDETMBEGA1UEAwwK +MTY1OTY2MjkzNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTwwOgYDVQQL +DDPmiJDpg73ph5HniZvmrKPmrKPnm7jnhafkupLogZTnvZHljLvpmaLmnInpmZDl +hazlj7gxCzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAPCINl3HwUEM2CzerR5u4GbUnvRJDjVmaEbm +qHlaYRUrrGDIF/Q63QEbcUh+PP+zZ0Grln7hGT5dwPj6e8jwmVvtEqO8mhBQzynH +6MZUONVfxj7L5Pf/+vZtB/PPGKdupZ9tUuodqOrbLycE8xWmSUDPrKEZ9iOEYamz +p0s9fPWwcZM8Vf4Gqx8d9ItnFJjVmF8aIJfsvcGNvKWD44JUzZItcsWz/srzxRAH +2moGIM8eNf1UNm3k6q43dfoRbZryWmVIyw49xybAKzm5e4Q4z8jCwGoQWgs2tSF4 +uM0/SxK2j1XRGnVmwRzcdDq/rCPlbqynHXZyUh8Te1540HJKZK8CAwEAAaOBuTCB +tjAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGH +hoGEaHR0cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0x +QkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0 +NzFCNjU0MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3 +DQEBCwUAA4IBAQAe4CfX/m0Xz6aI/Studd5pbdb/M7cyTjTwYSoBUNKtEmBwQFCu +CsFOEGuqmhq4ciMkooDWY/6mccgluZtdiYB8PQAIk1+IKBgpmhSuJP6yONecoit0 +JBWzFeSLkBxKkq6CfHhb9wW4EK0oPtgE6LjqLMj13fLy+Mxf4Eu4fgSlsdgGLpBt +bXIPbL12bCcP5hJLGRoLbECyx9LaOe1D9RAxwcTx/i+8C1s3IajkkyZnoPasLMs6 +cdSIp20QSct0klMgpEobmV+Z59NMKAQC/pbaSRrT/PDHUTDAuwA7W+COALg6H26c +MAGyeSTnwhc9WccmohKPgi3UcXh/Sn84QqSs +-----END CERTIFICATE----- diff --git a/extend/weChat/certs/1659662936/apiclient_key.pem b/extend/weChat/certs/1659662936/apiclient_key.pem new file mode 100644 index 0000000..0a65bca --- /dev/null +++ b/extend/weChat/certs/1659662936/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwiDZdx8FBDNgs +3q0ebuBm1J70SQ41ZmhG5qh5WmEVK6xgyBf0Ot0BG3FIfjz/s2dBq5Z+4Rk+XcD4 ++nvI8Jlb7RKjvJoQUM8px+jGVDjVX8Y+y+T3//r2bQfzzxinbqWfbVLqHajq2y8n +BPMVpklAz6yhGfYjhGGps6dLPXz1sHGTPFX+BqsfHfSLZxSY1ZhfGiCX7L3Bjbyl +g+OCVM2SLXLFs/7K88UQB9pqBiDPHjX9VDZt5OquN3X6EW2a8lplSMsOPccmwCs5 +uXuEOM/IwsBqEFoLNrUheLjNP0sSto9V0Rp1ZsEc3HQ6v6wj5W6spx12clIfE3te +eNBySmSvAgMBAAECggEBAOVndDqjLozFHFRHGGOjKgDJHsUr/BIwFpowmVQMP/V6 +DtDLZhU4ItpQew3B4JmbWIrIhSODupjBwC92bqLp3cpP5Gwnj+SpvbtCf57QatgO +nTv9KObizE9FE8WTqhbeL7ZLBT5mhVlhLKqRTOpECy92IlYQNbIQKzk4MAFRpqGH +CqSMtD0ng6DiMDxdQYLgIDCZIKamv0lF3rQymINQ6VZHpdlB0f3Gp/T/yWzn0nmJ +5eOrBhzFKGpevsp00FmvFE7HSqnogC7a3ieLluhfRPyB4QZQjfMorEGKwS8i7I8U +ZLA9FhtsaZpvOeNSZoVh3z0sblKUOvhXvL+jgRQK4PkCgYEA+M0QxM3VXst+deDr +ZkpmhWdPUQykVdSbfOdR8thYDu30QdjKe/T0X75wcqKh+s2GPK2GZsgeU02+EWqv +gVZMgFwGKFCBzDiCs9QwOTO+32OWP62yapFWNba1g4K7Un7MJRsJIj0u0nwjik68 +hec5KFYTOYttzk9oOLgXLFM6zm0CgYEA933lgvPITFykd2kGKg1v4oA52PPmq9LT +2dvVkDpIxioaUUHPALwFLhHLT+w/RPwCfP9gqZAQQFAfpMiTezm+ULA54rLHF2Tj +Lm0q3ko98sFW3Ltk1ehw8CdU0p5bii7a7AbuS6V36iTIrC53rhl7N+WL82uMw2w/ +a8Gz3fIl3gsCgYA+XsHeZC8iBWddS5YXXX1X4e8bRU0JCzQzWpXLh/qDO5mozBzu +eBiuy8HKqwRqKA2HtoRjzbT0cx+7o//9L1IcN3V/s7bmKCBzzjSMknE99OwcaIG6 +f1aaPoRARIyLAKhSgPWINMhBEcejC1vtQWqttu441cAgIP3ighulC/RI/QKBgQDE +lxKqdK8USTqzR4+H8+h+CNDqjsMalXuGwGLiEAoirur8xMODl9adg7D2KXkQeQYY ++Fp2FmNyUrLwGAtehL2yJmm0s8IFyTPUew7kSCDxJbaz2377k4mymet86iFYoGNx +vQeouyWHrfRhIQAcIU2JVyNWFoZX7TJrjBAuKtz9hQKBgGUFIyhQiPYRWDocvuSi +McyzYov2AnNehn24vsXC3SCXT804KPOX8RY4he63zWMGaLfCmSq1fhsJnf2pvffK +W+9ZXlIH6dwgQ9QyDNjKRVCGwTASsWOyqgn1Be0VDuuoXlREyGNQSn2RpvL1SMFH +PW/ePgdfoFkzU5WFJFPnvlU2 +-----END PRIVATE KEY----- diff --git a/extend/weChat/certs/1659662936/wechatpay_5B5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem b/extend/weChat/certs/1659662936/wechatpay_5B5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem new file mode 100644 index 0000000..290c3c2 --- /dev/null +++ b/extend/weChat/certs/1659662936/wechatpay_5B5C8A69CC86D1127F6B6AA06AAAF10531EEFE90.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUW1yKacyG0RJ/a2qgaqrxBTHu/pAwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjMxMjE5MDUxNTA3WhcNMjgxMjE3MDUxNTA3WjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGEwJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClp0R6MR6NwEkqRJNF +aD1INE0uHoRg1of0D8mTVwoWvADgt9gutpeJ1uTweAtrXkvu9NALxTNaG51tsZa8 +2fsJ00/Ry1IYUb6xG7kAuh0h4KLgWUUhoXDOKW0B1K/7g3AhCAylUzhD/ghD1Y8F +hlMtw87oNzfTE51I/2sGZgnPX2IqIEOjxx1F8Ebzfh5shJGN40guMpBItsCWYe7s +zj1zdyERL6zxQN2A7o2QvDeX1EsNIdwoEECv06tWjUEGJioFwb3OKBu5n9jJT+Og +EcJFmRasLRQWsooutsDCO7y5wIKrCY9n52eZgk9QlHkYWyiwUqpnrrJ1DC+ueJei +QptFAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB +kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv +aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 +RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 +MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAhWwscSAE1OBR3AGBOuFHs3vFJ+y37z5/ +EoB9FwVu4zXP1dih101zl83iwIdPEHIR2skXcTjHRI2qdOvu7X5JmWOJP+51RGtX +Y+aWXKfhRzRQOUlpNyltlgsGKzbIXLLBzQjMzBNv+n/HX4q9F0TV3SW4zTiMlhD8 ++bGGGwuIhziWpK9qvr3RPU1j+0bggHhIre+cNolnh1FepS4Gt964zhx6THtrS/jI +I64UBBh6moBq7zB5QYloBhW464c7GCEEv5/AdcxGhAe+vuL/mkVRNsSRxVPIxPE6 ++qoIiNBmQvL/mL+4UKfX6b9h4wrUQUdQP3ljRdpL3a5YTMTUJuoJSQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/extend/weChat/close.go b/extend/weChat/close.go new file mode 100644 index 0000000..857f93f --- /dev/null +++ b/extend/weChat/close.go @@ -0,0 +1,62 @@ +package weChat + +import ( + "context" + "errors" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" + "hepa-calc-admin-api/config" +) + +// CloseJsapiOrder 关闭jsapi支付订单 +func CloseJsapiOrder(outTradeNo string) error { + client, err := createClient() + if err != nil { + return err + } + + svc := jsapi.JsapiApiService{Client: client} + + req := jsapi.CloseOrderRequest{ + OutTradeNo: &outTradeNo, + Mchid: core.String(config.C.Wechat.Pay1281030301.MchId), + } + + result, err := svc.CloseOrder(context.TODO(), req) + if err != nil { + return err + } + + if result.Response.StatusCode != 204 { + return errors.New("关闭订单失败") + } + + return nil +} + +// CloseAppOrder 关闭app支付订单 +func CloseAppOrder(outTradeNo string) error { + client, err := createClient() + if err != nil { + return err + } + + svc := app.AppApiService{Client: client} + + req := app.CloseOrderRequest{ + OutTradeNo: &outTradeNo, + Mchid: core.String(config.C.Wechat.Pay1281030301.MchId), + } + + result, err := svc.CloseOrder(context.TODO(), req) + if err != nil { + return err + } + + if result.Response.StatusCode != 204 { + return errors.New("关闭订单失败") + } + + return nil +} diff --git a/extend/weChat/parseNotify.go b/extend/weChat/parseNotify.go new file mode 100644 index 0000000..b5250f4 --- /dev/null +++ b/extend/weChat/parseNotify.go @@ -0,0 +1,47 @@ +package weChat + +import ( + "context" + "errors" + "github.com/gin-gonic/gin" + "github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers" + "github.com/wechatpay-apiv3/wechatpay-go/core/downloader" + "github.com/wechatpay-apiv3/wechatpay-go/core/notify" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments" + "github.com/wechatpay-apiv3/wechatpay-go/utils" + "hepa-calc-admin-api/config" +) + +// ParseNotify 回调通知的验签与解密 +func ParseNotify(c *gin.Context) (notifyReq *notify.Request, t *payments.Transaction, err error) { + mchId := config.C.Wechat.Pay1281030301.MchId // 商户号 + mchCertificateSerialNumber := config.C.Wechat.Pay1281030301.MchCertificateSerialNumber // 商户证书序列号 + v3ApiSecret := config.C.Wechat.Pay1281030301.V3ApiSecret // 商户APIv3密钥 + privateKeyPath := "extend/weChat/certs/" + config.C.Wechat.Pay1281030301.MchId + "/apiclient_key.pem" // 商户私钥文件地址 + + // 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名 + mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath) + if err != nil { + return nil, nil, errors.New("微信支付生成失败") + } + + // 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器 + err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(c, mchPrivateKey, mchCertificateSerialNumber, mchId, v3ApiSecret) + if err != nil { + return nil, nil, err + } + + // 2. 获取商户号对应的微信支付平台证书访问器 + certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchId) + // 3. 使用证书访问器初始化 `notify.Handler` + handler, err := notify.NewRSANotifyHandler(v3ApiSecret, verifiers.NewSHA256WithRSAVerifier(certificateVisitor)) + + transaction := new(payments.Transaction) + notifyReq, err = handler.ParseNotifyRequest(context.Background(), c.Request, transaction) + // 如果验签未通过,或者解密失败 + if err != nil { + return nil, nil, err + } + + return notifyReq, transaction, nil +} diff --git a/extend/weChat/prepay.go b/extend/weChat/prepay.go new file mode 100644 index 0000000..7df6f33 --- /dev/null +++ b/extend/weChat/prepay.go @@ -0,0 +1,132 @@ +package weChat + +import ( + "context" + "errors" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" + "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" +) + +/** +JSAPI下单 +APP下单 +*/ + +// JsapiRequest 请求数据-JSAPI下单 +type JsapiRequest struct { + AppId string `json:"appid" comment:"公众号ID"` + MchId string `json:"mchid" comment:"直连商户号"` + Description string `json:"description" comment:"商品描述"` + OutTradeNo string `json:"out_trade_no" comment:"商户订单号"` + NotifyUrl string `json:"notify_url" comment:"回调地址"` + Amount JsapiRequestAmountRequest `json:"amount" comment:"订单金额"` + Payer JsapiRequestPayerRequest `json:"payer" comment:"支付者"` +} + +// JsapiRequestAmountRequest 订单金额信息 +type JsapiRequestAmountRequest struct { + Total int64 `json:"total" comment:"订单总金额"` + Currency string `json:"currency" comment:"货币类型"` +} + +// JsapiRequestPayerRequest 支付者信息 +type JsapiRequestPayerRequest struct { + OpenId string `json:"openid" comment:"openid"` +} + +// AppRequest 请求数据-APP下单 +type AppRequest struct { + AppId string `json:"appid" comment:"公众号ID"` + MchId string `json:"mchid" comment:"直连商户号"` + Description string `json:"description" comment:"商品描述"` + OutTradeNo string `json:"out_trade_no" comment:"商户订单号"` + NotifyUrl string `json:"notify_url" comment:"回调地址"` + Amount AppRequestAmountRequest `json:"amount" comment:"订单金额"` +} + +// AppRequestAmountRequest 订单金额信息 +type AppRequestAmountRequest struct { + Total int64 `json:"total" comment:"订单总金额"` + Currency string `json:"currency" comment:"货币类型"` +} + +// GetJsapiPrepay JSAPI下单-获取jsapi预支付交易会话 +func (r JsapiRequest) GetJsapiPrepay() (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { + client, err := createClient() + if err != nil { + return nil, err + } + + svc := jsapi.JsapiApiService{Client: client} + // 得到prepay_id,以及调起支付所需的参数和签名 + resp, result, err := svc.PrepayWithRequestPayment(context.Background(), + jsapi.PrepayRequest{ + Appid: core.String(r.AppId), + Mchid: core.String(r.MchId), + Description: core.String(r.Description), + OutTradeNo: core.String(r.OutTradeNo), + NotifyUrl: core.String(r.NotifyUrl), + Amount: &jsapi.Amount{ + Total: core.Int64(r.Amount.Total), + Currency: core.String("CNY"), + }, + Payer: &jsapi.Payer{ + Openid: core.String(r.Payer.OpenId), + }, + }, + ) + + if result.Response.StatusCode != 200 { + return nil, errors.New("发起支付失败") + } + + if resp.PrepayId == nil { + return nil, errors.New("发起支付失败") + } + + if err != nil { + return nil, err + } + + return resp, nil +} + +// GetAppPrepay APP下单-获取app预支付交易会话 +func (r AppRequest) GetAppPrepay() (prepay *app.PrepayWithRequestPaymentResponse, err error) { + client, err := createClient() + if err != nil { + return nil, err + } + + svc := app.AppApiService{Client: client} + // 得到prepay_id,以及调起支付所需的参数和签名 + + resp, result, err := svc.PrepayWithRequestPayment(context.Background(), + app.PrepayRequest{ + Appid: core.String(r.AppId), + Mchid: core.String(r.MchId), + Description: core.String(r.Description), + OutTradeNo: core.String(r.OutTradeNo), + NotifyUrl: core.String(r.NotifyUrl), + Amount: &app.Amount{ + Total: core.Int64(r.Amount.Total), + Currency: core.String("CNY"), + }, + }, + ) + + if result.Response.StatusCode != 200 { + return nil, errors.New("发起支付失败") + } + + if resp.PrepayId == nil { + return nil, errors.New("发起支付失败") + } + + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/extend/weChat/refund.go b/extend/weChat/refund.go new file mode 100644 index 0000000..86c1a02 --- /dev/null +++ b/extend/weChat/refund.go @@ -0,0 +1,46 @@ +package weChat + +import ( + "context" + "github.com/wechatpay-apiv3/wechatpay-go/core" + "github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic" +) + +type RefundRequest struct { + TransactionId string `json:"transaction_id" comment:"微信订单号"` + OutTradeNo string `json:"out_trade_no" comment:"商户订单号"` + OutRefundNo string `json:"out_refund_no" comment:"退款订单号"` + Reason string `json:"reason" comment:"退款原因"` + RefundAmount int64 `json:"refund_amount" comment:"退款金额"` + PaymentAmountTotal int64 `json:"payment_amount_total" comment:"支付金额"` + NotifyUrl string `json:"notify_url" comment:"回调地址"` +} + +// Refund 退款 +func (r RefundRequest) Refund() (*refunddomestic.Refund, error) { + client, err := createClient() + if err != nil { + return nil, err + } + + refundRequest := refunddomestic.CreateRequest{ + TransactionId: core.String(r.TransactionId), + OutTradeNo: core.String(r.OutTradeNo), + OutRefundNo: core.String(r.OutRefundNo), + Reason: core.String(r.Reason), + NotifyUrl: core.String(r.NotifyUrl), + Amount: &refunddomestic.AmountReq{ + Currency: core.String("CNY"), + Refund: core.Int64(r.RefundAmount), + Total: core.Int64(r.PaymentAmountTotal), + }, + } + + svc := refunddomestic.RefundsApiService{Client: client} + resp, _, err := svc.Create(context.Background(), refundRequest) + if err != nil { + return nil, err + } + + return resp, nil +} diff --git a/extend/weChat/userInfo.go b/extend/weChat/userInfo.go new file mode 100644 index 0000000..92b1131 --- /dev/null +++ b/extend/weChat/userInfo.go @@ -0,0 +1,76 @@ +package weChat + +import ( + "encoding/json" + "errors" + "io" + "net/http" +) + +// GetUserInfoResponse 网页授权拉取用户信息返回值 +type GetUserInfoResponse struct { + OpenId string `json:"openid" form:"openid" label:"openid"` + Nickname string `json:"nickname" form:"nickname" label:"用户昵称"` + Sex int `json:"sex" form:"sex" label:"性别"` // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 + Province string `json:"province" form:"province" label:"省份"` + City string `json:"city" form:"city" label:"城市"` + Country string `json:"country" form:"country" label:"国家"` + HeadImgUrl string `json:"headimgurl" form:"headimgurl" label:"头像"` + UnionId string `json:"unionid" form:"unionid" label:"unionid"` + Errcode *int `json:"errcode" form:"errcode" label:"errcode"` + Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"` +} + +// GetUserInfo 网页授权拉取用户信息 +func GetUserInfo(accessToken, openId string) (r *GetUserInfoResponse, err error) { + if accessToken == "" { + return nil, errors.New("授权失败") + } + + if openId == "" { + return nil, errors.New("授权失败") + } + + // 拼接请求数据 + requestUrl := "https://api.weixin.qq.com/sns/userinfo?" + + "access_token=" + accessToken + + "&openid=" + openId + + "&lang=zh_CN" + + // 发送GET请求 + resp, err := http.Get(requestUrl) + if err != nil { + return nil, err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != 200 { + return nil, errors.New("请求失败") + } + + // 读取响应体 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response GetUserInfoResponse + err = json.Unmarshal([]byte(respBody), &response) + if err != nil { + // json解析失败 + return nil, err + } + + if response.Errcode != nil { + if response.Errmsg != "" { + return nil, errors.New(response.Errmsg) + } else { + return nil, errors.New("请求失败") + } + } + + return &response, nil +} diff --git a/extend/weChat/webAccessToken.go b/extend/weChat/webAccessToken.go new file mode 100644 index 0000000..9a0bf4c --- /dev/null +++ b/extend/weChat/webAccessToken.go @@ -0,0 +1,68 @@ +package weChat + +import ( + "encoding/json" + "errors" + "hepa-calc-admin-api/config" + "io" + "net/http" +) + +// GetWebAccessTokenResponse 获取网页授权access_token返回值 +type GetWebAccessTokenResponse struct { + AccessToken string `json:"access_token" form:"access_token" label:"网页授权接口调用凭证"` + ExpiresIn int `json:"expires_in" form:"expires_in" label:"access_token接口调用凭证超时时间"` + RefreshToken string `json:"refresh_token" form:"refresh_token" label:"用户刷新access_token"` + OpenId string `json:"openid" form:"openid" label:"openid"` + Scope string `json:"scope" form:"scope" label:"scope"` + UnionId string `json:"unionid" form:"unionid" label:"unionid"` + Errcode *int `json:"errcode" form:"errcode" label:"errcode"` + Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"` +} + +// GetWebAccessToken 获取网页授权access_token +func GetWebAccessToken(code string) (r *GetWebAccessTokenResponse, err error) { + // 拼接请求数据 + requestUrl := "https://api.weixin.qq.com/sns/oauth2/access_token?" + + "appid=" + config.C.Wechat.AppId + + "&secret=" + config.C.Wechat.AppSecret + + "&code=" + code + + "&grant_type=authorization_code" + + // 发送GET请求 + resp, err := http.Get(requestUrl) + if err != nil { + return nil, err + } + + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + if resp.StatusCode != 200 { + return nil, errors.New("请求失败") + } + + // 读取响应体 + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response GetWebAccessTokenResponse + err = json.Unmarshal([]byte(respBody), &response) + if err != nil { + // json解析失败 + return nil, err + } + + if response.Errcode != nil { + if response.Errmsg != "" { + return nil, errors.New(response.Errmsg) + } else { + return nil, errors.New("请求失败") + } + } + + return &response, nil +} diff --git a/global/global.go b/global/global.go new file mode 100644 index 0000000..ac68306 --- /dev/null +++ b/global/global.go @@ -0,0 +1,24 @@ +package global + +import ( + "github.com/bwmarrin/snowflake" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" + "github.com/go-redis/redis/v8" + "github.com/rabbitmq/amqp091-go" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +// 全局变量 +var ( + Db *gorm.DB // 数据库 + Logger *logrus.Logger // 日志 + Redis *redis.Client // redis + Validate *validator.Validate // 验证器 + Trans ut.Translator // Validate/v10 全局验证器 + Snowflake *snowflake.Node // 雪花算法 + UserId int64 // 用户id + RabbitConn *amqp091.Connection // rabbitmq + RabbitChannel *amqp091.Channel // rabbitmq +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..464cf7c --- /dev/null +++ b/go.mod @@ -0,0 +1,100 @@ +module hepa-calc-admin-api + +go 1.19 + +require ( + github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8 + github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6 + github.com/alibabacloud-go/tea v1.2.2 + github.com/alibabacloud-go/tea-utils/v2 v2.0.6 + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/bwmarrin/snowflake v0.3.0 + github.com/facebookarchive/grace v0.0.0-20180706040059-75cf19382434 + github.com/fsnotify/fsnotify v1.7.0 + github.com/gen2brain/go-fitz v1.23.7 + github.com/gin-gonic/gin v1.10.0 + github.com/go-playground/locales v0.14.1 + github.com/go-playground/universal-translator v0.18.1 + github.com/go-playground/validator/v10 v10.22.0 + github.com/go-redis/redis/v8 v8.11.5 + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/mojocn/base64Captcha v1.3.6 + github.com/rabbitmq/amqp091-go v1.10.0 + github.com/robfig/cron/v3 v3.0.1 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/viper v1.19.0 + github.com/wechatpay-apiv3/wechatpay-go v0.2.18 + github.com/xuri/excelize/v2 v2.8.1 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.11 +) + +require ( + github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect + github.com/alibabacloud-go/debug v1.0.0 // indirect + github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect + github.com/alibabacloud-go/openapi-util v0.1.0 // indirect + github.com/alibabacloud-go/tea-utils v1.3.1 // indirect + github.com/alibabacloud-go/tea-xml v1.1.3 // indirect + github.com/aliyun/credentials-go v1.3.1 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect + github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect + github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect + github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 // indirect + github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2 // indirect + github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect + github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect + github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.3 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tjfoc/gmsm v1.3.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect + github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/image v0.14.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c6f7573 --- /dev/null +++ b/go.sum @@ -0,0 +1,332 @@ +github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= +github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= +github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8 h1:benoD0QHDrylMzEQVpX/6uKtrN8LohT66ZlKXVJh7pM= +github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI= +github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= +github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA= +github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6 h1:UTl97mt2qfavxveqCkaVg4tKaZUPzA9RKbFIRaIdtdg= +github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.6/go.mod h1:UWpcGrWwTbES9QW7OQ7xDffukMJ/l7lzioixIz8+lgY= +github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= +github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= +github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY= +github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= +github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= +github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= +github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= +github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA= +github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU= +github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/alibabacloud-go/tea-utils v1.3.1 h1:iWQeRzRheqCMuiF3+XkfybB3kTgUXkXX+JMrqfLeB2I= +github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= +github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.3/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ= +github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= +github.com/alibabacloud-go/tea-utils/v2 v2.0.6 h1:ZkmUlhlQbaDC+Eba/GARMPy6hKdCLiSke5RsN5LcyQ0= +github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= +github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= +github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= +github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28= +github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= +github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= +github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= +github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/facebookarchive/grace v0.0.0-20180706040059-75cf19382434 h1:AFIATPhFj7mrISc4z9zEpfm4a8UfwsCWzJ+Je5jA5Rs= +github.com/facebookarchive/grace v0.0.0-20180706040059-75cf19382434/go.mod h1:PY9iiFMrFjTzegsqfKCcb+Ekk5/j0ch0sMdBwlT6atI= +github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= +github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7VRjQhwPlR/v0glZXNYzBHdNUzf/Am2Nmg= +github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg= +github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434 h1:mOp33BLbcbJ8fvTAmZacbBiOASfxN+MLcLxymZCIrGE= +github.com/facebookgo/grace v0.0.0-20180706040059-75cf19382434/go.mod h1:KigFdumBXUPSwzLDbeuzyt0elrL7+CP7TKuhrhT4bcU= +github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2 h1:nXeeRHmgNgjLxi+7dY9l9aDvSS1uwVlNLqUWIY4Ath0= +github.com/facebookgo/httpdown v0.0.0-20180706035922-5979d39b15c2/go.mod h1:TUV/fX3XrTtBQb5+ttSUJzcFgLNpILONFTKmBuk5RSw= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 h1:0YtRCqIZs2+Tz49QuH6cJVw/IFqzo39gEqZ0iYLxD2M= +github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4/go.mod h1:vsJz7uE339KUCpBXx3JAJzSRH7Uk4iGGyJzR529qDIA= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gen2brain/go-fitz v1.23.7 h1:HPhzEVzmOINvCKqQgB/DwMzYh4ArIgy3tMwq1eJTcbg= +github.com/gen2brain/go-fitz v1.23.7/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +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.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw= +github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= +github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= +github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wechatpay-apiv3/wechatpay-go v0.2.18 h1:vj5tvSmnEIz3ZsnFNNUzg+3Z46xgNMJbrO4aD4wP15w= +github.com/wechatpay-apiv3/wechatpay-go v0.2.18/go.mod h1:A254AUBVB6R+EqQFo3yTgeh7HtyqRRtN2w9hQSOrd4Q= +github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0= +github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ= +github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE= +github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4= +github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= +golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= +golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= +gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..b981110 --- /dev/null +++ b/main.go @@ -0,0 +1,60 @@ +package main + +import "C" +import ( + "fmt" + "github.com/facebookarchive/grace/gracehttp" + "hepa-calc-admin-api/api/router" + "hepa-calc-admin-api/config" + "hepa-calc-admin-api/core" + "hepa-calc-admin-api/extend/rabbitMq" + "net/http" + "strconv" +) + +func main() { + // 加载配置文件 + core.Viper() + + // 加载日志 + core.Logrus() + + // 加载数据库 + core.Mysql() + + // 加载redis缓存 + core.Redis() + + // 加载验证器 + core.Validator() + + // 加载雪花算法 + core.Snowflake() + + // 确保在应用关闭时关闭 RabbitMQ 连接和通道 + defer rabbitMq.Close() + + // 启动rabbitmq + core.StartRabbitMq() + + // 启动rabbitmq消费者端 + core.StartRabbitMqConsume() + + // 加载定时器-定时器需要在队列后方,不然可能会出现生产失败的问题 + core.StartCron() + + // 初始化路由-加载中间件 + r := router.Init() + + // 启动 HTTP 服务器 + server := &http.Server{ + Addr: ":" + strconv.Itoa(config.C.Port), // 设置服务器监听的端口号 + Handler: r, + } + + // 使用 grace 运行服务器 + err := gracehttp.Serve(server) + if err != nil { + fmt.Printf("启动失败:%v\n\n", err) + } +} diff --git a/utils/aes.go b/utils/aes.go new file mode 100644 index 0000000..ba70469 --- /dev/null +++ b/utils/aes.go @@ -0,0 +1,90 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "io" +) + +// Encrypt 加密函数 +func Encrypt(text, key string) (string, error) { + plaintext := []byte(text) + hashedKey := hashKey(key) + + // 创建一个新的 AES cipher.Block + block, err := aes.NewCipher(hashedKey) + if err != nil { + return "", err + } + + // 使用 GCM 模式加密 + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonce := make([]byte, aesGCM.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + // 加密数据 + ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil) + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +// Decrypt 解密函数 +func Decrypt(cryptoText, key string) (string, error) { + ciphertext, _ := base64.StdEncoding.DecodeString(cryptoText) + hashedKey := hashKey(key) + + // 创建一个新的 AES cipher.Block + block, err := aes.NewCipher(hashedKey) + if err != nil { + return "", err + } + + // 使用 GCM 模式解密 + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + + nonceSize := aesGCM.NonceSize() + nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] + + // 解密数据 + plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", err + } + + return string(plaintext), nil +} + +// HashString 使用SHA256算法对字符串进行哈希加密 +func HashString(s string) string { + // 创建一个新的SHA256哈希实例 + hasher := sha256.New() + + // 写入要哈希的字符串的字节 + hasher.Write([]byte(s)) + + // 获取哈希结果的字节切片 + hashBytes := hasher.Sum(nil) + + // 将字节切片转换成十六进制字符串表示 + s = hex.EncodeToString(hashBytes) + + return s +} + +// 将任意长度的密钥转换为适合 AES 的长度(32 字节) +func hashKey(key string) []byte { + hash := sha256.Sum256([]byte(key)) + return hash[:] +} diff --git a/utils/captcha.go b/utils/captcha.go new file mode 100644 index 0000000..6f502f4 --- /dev/null +++ b/utils/captcha.go @@ -0,0 +1,51 @@ +package utils + +import ( + "github.com/mojocn/base64Captcha" + "image/color" + "time" +) + +// GenerateCaptcha 生成验证码-base64 +func GenerateCaptcha() (id, b64s string, err error) { + var driver *base64Captcha.DriverString + // 配置验证码的参数 + driverString := &base64Captcha.DriverString{ + Height: 40, + Width: 100, + NoiseCount: 0, + ShowLineOptions: 0, + Length: 4, + Source: "1234567890", + BgColor: &color.RGBA{R: 3, G: 102, B: 214, A: 125}, + Fonts: []string{"wqy-microhei.ttc"}, + } + // ConvertFonts 按名称加载字体 + driver = driverString.ConvertFonts() + + base64Captcha.Expiration = 30 * time.Minute + store := base64Captcha.DefaultMemStore + + captcha := base64Captcha.NewCaptcha(driver, store) + id, b64s, _, err = captcha.Generate() + if err != nil { + return "", "", err + } + return id, b64s, nil +} + +// VerifyCaptcha 验证验证码 +func VerifyCaptcha(id, answer string) bool { + // 创建验证码实例 + base64Captcha.Expiration = 30 * time.Minute + store := base64Captcha.DefaultMemStore + captcha := base64Captcha.NewCaptcha(nil, store) + + // 验证验证码 + isValid := captcha.Verify(id, answer, true) + if !isValid { + return false + } + + return true +} diff --git a/utils/compute.go b/utils/compute.go new file mode 100644 index 0000000..1370d56 --- /dev/null +++ b/utils/compute.go @@ -0,0 +1,85 @@ +package utils + +import ( + "fmt" + "github.com/gen2brain/go-fitz" + "image/jpeg" + "os" + "path/filepath" +) + +// 一些计算 + +// ComputeIndividualIncomeTax 计算个人所得税 +func ComputeIndividualIncomeTax(income float64) float64 { + if income <= 800 { + return 0 + } + + if income <= 4000 { + income = income - 800 + } + + // 实际纳税金额 + if income > 4000 { + income = income * 0.8 + } + + // 税率、速算扣除数 + var taxRate, quickDeduction float64 + + if income <= 20000 { + taxRate = 0.2 + quickDeduction = 0 + } else if income <= 50000 { + taxRate = 0.3 + quickDeduction = 2000 + } else { + taxRate = 0.4 + quickDeduction = 7000 + } + + incomeTax := income*taxRate - quickDeduction + + return incomeTax +} + +// ConvertPDFToImages converts a PDF file to images and saves them in the specified output directory. +func ConvertPDFToImages(pdfPath string, outputDir string, filename string) error { + // Open the PDF file + doc, err := fitz.New(pdfPath) + if err != nil { + return fmt.Errorf("failed to open PDF file: %v", err) + } + defer doc.Close() + + // Ensure the output directory exists + if err := os.MkdirAll(outputDir, os.ModePerm); err != nil { + return fmt.Errorf("failed to create output directory: %v", err) + } + + // Iterate over each page in the PDF + for i := 0; i < doc.NumPage(); i++ { + // Render the page to an image + img, err := doc.Image(i) + if err != nil { + return fmt.Errorf("failed to render page %d: %v", i, err) + } + + // Create the output file + outputFile := filepath.Join(outputDir, filename) + file, err := os.Create(outputFile) + if err != nil { + return fmt.Errorf("failed to create output file: %v", err) + } + defer file.Close() + + // Encode the image as JPEG and save it to the file + opts := &jpeg.Options{Quality: 80} + if err := jpeg.Encode(file, img, opts); err != nil { + return fmt.Errorf("failed to encode image: %v", err) + } + } + + return nil +} diff --git a/utils/directory.go b/utils/directory.go new file mode 100644 index 0000000..dcb0a5a --- /dev/null +++ b/utils/directory.go @@ -0,0 +1,21 @@ +package utils + +import ( + "errors" + "os" +) + +// PathExists 文件是否存在 +func PathExists(path string) (bool, error) { + fi, err := os.Stat(path) + if err == nil { + if fi.IsDir() { + return true, nil + } + return false, errors.New("存在同名文件") + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} diff --git a/utils/export.go b/utils/export.go new file mode 100644 index 0000000..a726458 --- /dev/null +++ b/utils/export.go @@ -0,0 +1,298 @@ +package utils + +import ( + "bytes" + "fmt" + "github.com/xuri/excelize/v2" + "reflect" + "strconv" +) + +// func Export(widths []int) (bool, error) { +// f := excelize.NewFile() +// defer func() { +// _ = f.Close() +// }() +// +// // 创建一个工作表 +// index, err := f.NewSheet("Sheet1") +// if err != nil { +// return false, err +// } +// +// // 设置工作簿的默认工作表 +// f.SetActiveSheet(index) +// +// // 单元格对齐样式 +// alignment := &excelize.Alignment{ +// Horizontal: "center", +// Vertical: "center", +// } +// +// // 单元格颜色填充样式 +// fill := excelize.Fill{ +// Type: "pattern", +// Pattern: 1, +// Color: []string{"#c9daf8"}, +// Shading: 0, +// } +// +// // 第一行的左、右、下边框 +// border := []excelize.Border{ +// { +// Type: "left,right,bottom", +// Color: "", +// Style: 1, +// }, +// } +// +// // 工作表样式 +// style, err := f.NewStyle( +// &excelize.Style{ +// Fill: fill, +// Alignment: alignment, +// Border: border, +// }, +// ) +// if err != nil { +// return false, err +// } +// +// // 依次设置每一列的列宽 +// widths = []int{18, 18, 18, 18, 18, 20, 23, 46, 18, 30, 30, 18, 18, 30, 18, 30} +// for col, width := range widths { +// // 获取列名 +// colName, err := excelize.ColumnNumberToName(col + 1) +// if err != nil { +// return false, err +// } +// +// // 设置列宽 +// err = f.SetColWidth("Sheet1", colName, colName, float64(width)) +// if err != nil { +// return false, err +// } +// +// // 设置列背景颜色 +// err = f.SetCellStyle("Sheet1", colName+"1", colName+"1", style) +// if err != nil { +// return false, err +// } +// } +// +// // 设置行高 +// err = f.SetRowStyle("Sheet1", 1, 10, style) +// if err != nil { +// return false, err +// } +// +// if err := f.SaveAs("output.xlsx"); err != nil { +// return false, err +// } +// +// return true, nil +// } + +// HeaderCellData 表头内容 +type HeaderCellData struct { + Value string // 值 + CellType string // 类型 + NumberFmt string // 格式化方式 + ColWidth int // 列宽 +} + +func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) { + sheetName := "Sheet1" + + f := excelize.NewFile() + defer func() { + _ = f.Close() + }() + + // 创建一个工作表 + index, err := f.NewSheet(sheetName) + if err != nil { + return nil, err + } + + // 设置工作簿的默认工作表 + f.SetActiveSheet(index) + + // 设置工作表默认字体 + err = f.SetDefaultFont("宋体") + if err != nil { + return nil, err + } + + // 统一单元格对齐样式 + alignment := &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, 1, 35) + if err != nil { + return nil, err + } + + // 处理工作表表头 + for c, cell := range header { + // 获取列名 + colName, err := excelize.ColumnNumberToName(c + 1) + if err != nil { + return nil, err + } + + // 添加单元格的值 + err = f.SetCellValue(sheetName, colName+"1", cell.Value) + if err != nil { + return nil, err + } + + // 单元格颜色填充样式 + fill := excelize.Fill{ + Type: "pattern", + Pattern: 1, + Color: []string{"#c9daf8"}, + Shading: 0, + } + + // 第一行的左、右、下边框 + border := []excelize.Border{ + { + Type: "left", + Color: "#000000", + Style: 1, + }, + { + Type: "right", + Color: "#000000", + Style: 1, + }, + { + Type: "bottom", + Color: "#000000", + Style: 1, + }, + } + + // 设置单元格值类型和格式 + style, _ := f.NewStyle(&excelize.Style{ + Alignment: alignment, // 字体居中 + Fill: fill, // 背景颜色 + Border: border, // 边框 + }) + err = f.SetCellStyle(sheetName, colName+"1", colName+"1", style) + if err != nil { + return nil, err + } + + // 设置列宽 + err = f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)) + if err != nil { + return nil, err + } + } + + // 设置单元格格式 + row := len(data) + for i, cell := range header { + // 获取列名 + colName, err := excelize.ColumnNumberToName(i + 1) + if err != nil { + return nil, err + } + + // 字体居中 + style := &excelize.Style{} + style = &excelize.Style{ + Alignment: alignment, + } + + if cell.CellType == "float" { + style.NumFmt = 2 + customNumFmt := "0.000" + style.CustomNumFmt = &customNumFmt + } + + if cell.CellType == "date" { + style.NumFmt = 22 + customNumFmt := "yyyy-mm-dd hh:mm:ss" + style.CustomNumFmt = &customNumFmt + } + + newStyle, _ := f.NewStyle(style) + err = f.SetCellStyle(sheetName, colName+"2", colName+strconv.Itoa(row), newStyle) + if err != nil { + return nil, err + } + } + + // 填充数据 + for r, rowData := range data { + rv := reflect.ValueOf(rowData) + for c := 0; c < rv.NumField(); c++ { + cellValue := rv.Field(c).Interface() + // 获取列名 + colName, err := excelize.ColumnNumberToName(c + 1) + if err != nil { + return nil, err + } + + axis := colName + fmt.Sprintf("%d", r+2) + + // 设置单元格值 + err = f.SetCellValue(sheetName, axis, cellValue) + if err != nil { + return nil, err + } + + // 设置单元格值类型 + cellType := header[c].CellType + + // 字体居中 + style := &excelize.Style{} + style = &excelize.Style{ + Alignment: alignment, + } + + if cellType == "float" { + style.NumFmt = 2 + customNumFmt := "0.000" + style.CustomNumFmt = &customNumFmt + } + + if cellType == "date" { + style.NumFmt = 22 + customNumFmt := "yyyy-mm-dd hh:mm:ss" + style.CustomNumFmt = &customNumFmt + } + + newStyle, _ := f.NewStyle(style) + err = f.SetCellStyle(sheetName, axis, axis, newStyle) + if err != nil { + return nil, err + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, r+2, 35) + if err != nil { + return nil, err + } + } + } + + buffer, err := f.WriteToBuffer() + if err != nil { + return nil, err + } + + return buffer, nil + + // 保存文件 + // if err := f.SaveAs("output.xlsx"); err != nil { + // return nil, err + // } + // return nil, errors.New("已导出文件") +} diff --git a/utils/idCard.go b/utils/idCard.go new file mode 100644 index 0000000..72cdafd --- /dev/null +++ b/utils/idCard.go @@ -0,0 +1,124 @@ +// Package utils 身份证处理 +package utils + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" +) + +// GetCardAge 获取身份证年龄 +func GetCardAge(cardNum string) (int, error) { + // 获取当前时间 + now := time.Now() + + // 解析身份证号中的出生日期 + birthDateStr := cardNum[6:14] + birthYear, err := strconv.Atoi(birthDateStr[0:4]) + if err != nil { + return 0, err + } + birthMonth, err := strconv.Atoi(birthDateStr[4:6]) + if err != nil { + return 0, err + } + + // 计算年龄 + age := now.Year() - birthYear + if now.Month() < time.Month(birthMonth) { + age-- + } + + return age, nil +} + +// CheckCardNum 检测身份证号 +func CheckCardNum(cardNum string) (bool, error) { + fmt.Println(cardNum) + regex := `^(?:1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5])\d{4}(?:1[89]|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dxX]$` + match, err := regexp.MatchString(regex, cardNum) + fmt.Println(match) + if !match || err != nil { + return false, errors.New("身份证号错误") + } + return true, nil +} + +// GetCardSex 获取身份证性别 +func GetCardSex(cardNum string) (int, error) { + genderStr := cardNum[len(cardNum)-2 : len(cardNum)-1] + genderNum, err := strconv.Atoi(genderStr) + if err != nil { + return 0, err + } + + // 判断性别 + if genderNum%2 == 0 { + return 2, nil + } else { + return 1, nil + } +} + +// GetMaskCardNum 身份证号码脱敏 +func GetMaskCardNum(cardNum string) string { + if len(cardNum) != 18 { + return cardNum + } + + // 获取身份证号前后部分 + frontPart := cardNum[0:6] + backPart := cardNum[14:] + + // 替换中间部分数字为 * + middlePart := "****" + + // 拼接新的身份证号 + maskedIDCard := frontPart + middlePart + backPart + + return maskedIDCard +} + +// GetMaskCardName 身份证名字脱敏 +func GetMaskCardName(cardName string) string { + // 判断姓名长度 + length := utf8.RuneCountInString(cardName) + + // 判断是否是英文姓名 + isEnglish := strings.ContainsAny(cardName, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + + if length == 2 { + // 两个字符的姓名 + if isEnglish { + // 英文姓名 + return string(cardName[0]) + "*" + } else { + // 中文姓名 + return string([]rune(cardName)[0]) + "*" + } + } else if length == 3 { + // 三个字符的姓名 + if isEnglish { + // 英文姓名 + return string(cardName[0]) + "*" + string(cardName[2]) + } else { + // 中文姓名 + return string([]rune(cardName)[0]) + "*" + string([]rune(cardName)[2]) + } + } else if length >= 4 { + // 四个及以上字符的姓名 + if isEnglish { + // 英文姓名 + return string(cardName[0]) + "*" + string(cardName[length-1]) + } else { + // 中文姓名 + return string([]rune(cardName)[0]) + "*" + string([]rune(cardName)[length-1]) + } + } + + return cardName +} diff --git a/utils/intToString.go b/utils/intToString.go new file mode 100644 index 0000000..c760f47 --- /dev/null +++ b/utils/intToString.go @@ -0,0 +1,29 @@ +package utils + +// OrderCancelReasonToString 订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) +func OrderCancelReasonToString(i int) string { + switch i { + case 1: + return "主动取消" + case 2: + return "后台取消" + case 3: + return "支付超时取消" + default: + return "取消" + } +} + +// OrderSingleStatusToString 订单状态(1:待支付 2:已完成 3:已取消) +func OrderSingleStatusToString(i int) string { + switch i { + case 1: + return "待支付" + case 2: + return "已完成" + case 3: + return "已取消" + default: + return "未知" + } +} diff --git a/utils/jwt.go b/utils/jwt.go new file mode 100644 index 0000000..07841bb --- /dev/null +++ b/utils/jwt.go @@ -0,0 +1,40 @@ +package utils + +import ( + "github.com/golang-jwt/jwt/v5" + "hepa-calc-admin-api/config" + "time" +) + +type Token struct { + UserId string `json:"user_id"` // 用户id + jwt.RegisteredClaims // v5版本新加的方法 +} + +// NewJWT GenerateJWT 生成JWT +func (t Token) NewJWT() (string, error) { + ttl := time.Duration(config.C.Jwt.Ttl) + + t.RegisteredClaims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(ttl * time.Hour)) // 过期时间24小时 + t.RegisteredClaims.IssuedAt = jwt.NewNumericDate(time.Now()) // 签发时间 + t.RegisteredClaims.NotBefore = jwt.NewNumericDate(time.Now()) // 生效时间 + + // 使用HS256签名算法 + token := jwt.NewWithClaims(jwt.SigningMethodHS256, t) + s, err := token.SignedString([]byte(config.C.Jwt.SignKey)) + + return s, err +} + +// ParseJwt 解析JWT +func ParseJwt(authorization string) (*Token, error) { + t, err := jwt.ParseWithClaims(authorization, &Token{}, func(token *jwt.Token) (interface{}, error) { + return []byte(config.C.Jwt.SignKey), nil + }) + + if claims, ok := t.Claims.(*Token); ok && t.Valid { + return claims, nil + } else { + return nil, err + } +} diff --git a/utils/logger.go b/utils/logger.go new file mode 100644 index 0000000..a3fd7f8 --- /dev/null +++ b/utils/logger.go @@ -0,0 +1,28 @@ +package utils + +import ( + "encoding/json" + "fmt" + "github.com/sirupsen/logrus" + "hepa-calc-admin-api/global" +) + +func LogJsonInfo(msg string, v interface{}) { + jsonData, err := json.Marshal(v) + if err != nil { + fmt.Println("Error marshaling struct to JSON:", err) + return + } + + jsonString := string(jsonData) + + global.Logger.WithFields(logrus.Fields{ + "data": jsonString, + }).Info(msg) +} + +func LogJsonErr(msg string, v interface{}) { + global.Logger.WithFields(logrus.Fields{ + "data": v, + }).Errorf(msg) +} diff --git a/utils/mask.go b/utils/mask.go new file mode 100644 index 0000000..c3317f5 --- /dev/null +++ b/utils/mask.go @@ -0,0 +1,89 @@ +package utils + +import ( + "regexp" + "unicode/utf8" +) + +// MaskNameStr 用户名掩码 +func MaskNameStr(str string, maskType int) string { + if str == "" { + return str + } + + // 使用正则表达式判断是否包含中文字符 + chinesePattern := "[\u4e00-\u9fa5]+" + isChinese, _ := regexp.MatchString(chinesePattern, str) + + // 使用正则表达式判断是否包含英文字母 + englishPattern := "[A-Za-z]+" + isEnglish, _ := regexp.MatchString(englishPattern, str) + + // 判断是否包含中文字符 + if isChinese { + // 按照中文字符计算长度 + strLen := utf8.RuneCountInString(str) + + if strLen >= 3 { + if maskType == 1 { + // 三个字符或三个字符以上掐头取尾,中间用*代替 + firstChar, _ := utf8.DecodeRuneInString(str) + lastChar, _ := utf8.DecodeLastRuneInString(str) + str = string(firstChar) + "*" + string(lastChar) + } else { + // 首字母保留,后两位用*代替 + firstChar, _ := utf8.DecodeRuneInString(str) + str = string(firstChar) + "**" + } + } else if strLen == 2 { + // 两个字符 + firstChar, _ := utf8.DecodeRuneInString(str) + str = string(firstChar) + "*" + } + } else if isEnglish { + // 按照英文字串计算长度 + strLen := utf8.RuneCountInString(str) + + if strLen >= 3 { + if maskType == 1 { + // 三个字符或三个字符以上掐头取尾,中间用*代替 + firstChar, _ := utf8.DecodeRuneInString(str) + lastChar, _ := utf8.DecodeLastRuneInString(str) + str = string(firstChar) + "*" + string(lastChar) + } else { + // 首字母保留,后两位用*代替 + firstChar, _ := utf8.DecodeRuneInString(str) + str = string(firstChar) + "**" + } + } else if strLen == 2 { + // 两个字符 + firstChar, _ := utf8.DecodeRuneInString(str) + str = string(firstChar) + "*" + } + } + + return str +} + +// MaskPhoneStr 手机号、固话加密 +// 固话:0510-89754815 0510-8****815 +// 手机号:18221234158 18*******58 +func MaskPhoneStr(phone string) string { + if phone == "" { + return phone + } + + // 使用正则表达式匹配固定电话 + phonePattern := `(0[0-9]{2,3}[\-]?[2-9][0-9]{6,7}[\-]?[0-9]?)` + isFixedLine := regexp.MustCompile(phonePattern).MatchString(phone) + + if isFixedLine { + // 匹配到固定电话 + // 替换匹配的部分为固定格式 + return regexp.MustCompile(`(0[0-9]{2,3}[\-]?[2-9])[0-9]{3,4}([0-9]{3}[\-]?[0-9]?)`).ReplaceAllString(phone, "$1****$2") + } else { + // 匹配到手机号 + // 替换匹配的部分为固定格式 + return regexp.MustCompile(`(1[0-9]{1})[0-9]{7}([0-9]{2})`).ReplaceAllString(phone, "$1*******$2") + } +} diff --git a/utils/regular.go b/utils/regular.go new file mode 100644 index 0000000..467f4a5 --- /dev/null +++ b/utils/regular.go @@ -0,0 +1,12 @@ +package utils + +import "regexp" + +// RegexpMobile 手机号匹配 +func RegexpMobile(mobile string) bool { + ok, err := regexp.MatchString(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`, mobile) + if err != nil { + return false + } + return ok +} diff --git a/utils/replace.go b/utils/replace.go new file mode 100644 index 0000000..6334b51 --- /dev/null +++ b/utils/replace.go @@ -0,0 +1,22 @@ +package utils + +import ( + "hepa-calc-admin-api/config" + "strings" +) + +// RemoveOssDomain 去除oss地址中的前缀 +func RemoveOssDomain(url string) string { + if url != "" { + url = strings.Replace(url, config.C.Oss.OssCustomDomainName, "", 1) + } + return url +} + +// AddOssDomain 增加oss地址中的前缀 +func AddOssDomain(url string) string { + if url == "" { + return "" + } + return config.C.Oss.OssCustomDomainName + url +} diff --git a/utils/validator.go b/utils/validator.go new file mode 100644 index 0000000..5e9aa56 --- /dev/null +++ b/utils/validator.go @@ -0,0 +1,15 @@ +package utils + +import ( + "github.com/go-playground/validator/v10" + "hepa-calc-admin-api/global" +) + +// Translate 检验并返回检验错误信息 +func Translate(err error) (errMsg string) { + errs := err.(validator.ValidationErrors) + for _, err := range errs { + errMsg = err.Translate(global.Trans) + } + return +}