Files
jackyu66gitandCursor 7155b8b53a feat(api): 接入微信登录并原生实现咨询域(ECR-049/050)
小程序可在 Go 上完成微信手机号登录、测评、预约和下单,不再反代 Java。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 00:26:29 +08:00

385 lines
10 KiB
Go

package handler
import (
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/yuxingu/digital-psychology/apps/api/internal/middleware"
"github.com/yuxingu/digital-psychology/apps/api/internal/service/consult"
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
)
// ConsultHandler serves /api/v1/psychic/* (ECR-050).
type ConsultHandler struct {
Svc *consult.Service
}
func (h *ConsultHandler) Register(api *gin.RouterGroup) {
g := api.Group("/psychic")
g.POST("/banner/all", h.banners)
g.GET("/news/page", h.newsPage)
g.GET("/news/get", h.newsGet)
g.POST("/procotol/getByCode", h.protocol)
g.POST("/study/list", h.studyList)
g.POST("/study/getSingleDetail", h.studyDetail)
g.POST("/user-choice/save", h.choiceSave)
g.POST("/user-choice/getResult", h.choiceResult)
g.POST("/user-choice/my-test", h.myTest)
g.GET("/doctor-info/business-scope-list", h.scopes)
g.GET("/doctor-info/page", h.doctorPage)
g.GET("/doctor-info/get", h.doctorGet)
g.POST("/doctor-info/focus", h.focusOn)
g.POST("/doctor-info/cancel-focus", h.focusOff)
g.GET("/doctor-info/get-show-info", h.showInfo)
g.POST("/appointment/remain-list", h.remain)
g.POST("/appointment/date-detail-list", h.dateDetail)
g.POST("/order/create", h.orderCreate)
g.GET("/order/get-pay-param", h.payParam)
g.POST("/order/order-list", h.orderList)
g.POST("/order/order-detail", h.orderDetail)
g.POST("/order/cancel", h.orderCancel)
g.POST("/order/delete-order", h.orderDelete)
g.POST("/pay/createOrder", h.payDemo)
g.POST("/pay/refund", h.payRefund)
g.POST("/pay/notify", h.payNotify)
g.GET("/platform-user/getSelfInfo", h.selfInfo)
g.POST("/platform-user/update", h.updateSelf)
g.GET("/platform-user/not-read-num", h.notRead)
g.POST("/platform-user/focus-to-read", h.focusRead)
g.POST("/platform-user/order-to-read", h.orderRead)
g.POST("/platform-user/focus-all", h.focusAll)
g.POST("/platform-user/user-feedback", h.feedback)
g.POST("/platform-user/feedback-flag", h.feedbackFlag)
}
func (h *ConsultHandler) banners(c *gin.Context) {
data, err := h.Svc.Banners(c.Request.Context())
h.ok(c, data, err)
}
func (h *ConsultHandler) newsPage(c *gin.Context) {
data, err := h.Svc.NewsPage(c.Request.Context(), qInt(c, "type", 0), qInt(c, "showMain", -1), qInt(c, "pageNo", 1), qInt(c, "pageSize", 20))
h.ok(c, data, err)
}
func (h *ConsultHandler) ok(c *gin.Context, data any, err error) {
if err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, data)
}
func (h *ConsultHandler) newsGet(c *gin.Context) {
data, err := h.Svc.NewsGet(c.Request.Context(), qInt64(c, "id"))
h.ok(c, data, err)
}
func (h *ConsultHandler) protocol(c *gin.Context) {
data, err := h.Svc.ProtocolByCode(c.Request.Context(), c.Query("code"))
h.ok(c, data, err)
}
func (h *ConsultHandler) studyList(c *gin.Context) {
data, err := h.Svc.TestList(c.Request.Context(), qInt(c, "showMain", -1))
h.ok(c, data, err)
}
func (h *ConsultHandler) studyDetail(c *gin.Context) {
data, err := h.Svc.TestDetail(c.Request.Context(), qInt64(c, "studyId"))
h.ok(c, data, err)
}
func (h *ConsultHandler) choiceSave(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var in consult.ChoiceIn
if err := c.ShouldBindJSON(&in); err != nil {
response.Fail(c, http.StatusBadRequest, 10000, "invalid request")
return
}
data, err := h.Svc.SaveChoice(c.Request.Context(), uid, in)
h.ok(c, data, err)
}
func (h *ConsultHandler) choiceResult(c *gin.Context) {
data, err := h.Svc.GetResult(c.Request.Context(), qInt64(c, "resultId"))
h.ok(c, data, err)
}
func (h *ConsultHandler) myTest(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var p struct {
PageNo int `json:"pageNo"`
PageSize int `json:"pageSize"`
}
_ = c.ShouldBindJSON(&p)
data, err := h.Svc.MyTests(c.Request.Context(), uid, p.PageNo, p.PageSize)
h.ok(c, data, err)
}
func (h *ConsultHandler) scopes(c *gin.Context) {
data, err := h.Svc.Scopes(c.Request.Context())
h.ok(c, data, err)
}
func (h *ConsultHandler) doctorPage(c *gin.Context) {
data, err := h.Svc.DoctorPage(c.Request.Context(), c.Query("businessScope"), qInt(c, "isTop", -1), qInt(c, "pageNo", 1), qInt(c, "pageSize", 20))
h.ok(c, data, err)
}
func (h *ConsultHandler) doctorGet(c *gin.Context) {
uid, _ := middleware.UserIDFromContext(c)
data, err := h.Svc.DoctorGet(c.Request.Context(), qInt64(c, "id"), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) focusOn(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
if err := h.Svc.SetFocus(c.Request.Context(), uid, qInt64(c, "id"), true); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) focusOff(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
if err := h.Svc.SetFocus(c.Request.Context(), uid, qInt64(c, "id"), false); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) showInfo(c *gin.Context) {
uid, _ := middleware.UserIDFromContext(c)
data, err := h.Svc.ShowInfo(c.Request.Context(), qInt64(c, "id"), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) remain(c *gin.Context) {
var in struct {
DoctorID int64 `json:"doctorId"`
}
_ = c.ShouldBindJSON(&in)
data, err := h.Svc.RemainList(c.Request.Context(), in.DoctorID)
h.ok(c, data, err)
}
func (h *ConsultHandler) dateDetail(c *gin.Context) {
var in struct {
DoctorID int64 `json:"doctorId"`
}
_ = c.ShouldBindJSON(&in)
data, err := h.Svc.DateDetail(c.Request.Context(), in.DoctorID)
h.ok(c, data, err)
}
func (h *ConsultHandler) orderCreate(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var in consult.OrderIn
if err := c.ShouldBindJSON(&in); err != nil {
response.Fail(c, http.StatusBadRequest, 10000, "invalid request")
return
}
data, err := h.Svc.CreateOrder(c.Request.Context(), uid, in)
h.ok(c, data, err)
}
func (h *ConsultHandler) payParam(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.PayParam(c.Request.Context(), uid, c.Query("orderSn"))
h.ok(c, data, err)
}
func (h *ConsultHandler) orderList(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var p struct {
PageNo int `json:"pageNo"`
PageSize int `json:"pageSize"`
}
_ = c.ShouldBindJSON(&p)
data, err := h.Svc.OrderList(c.Request.Context(), uid, p.PageNo, p.PageSize)
h.ok(c, data, err)
}
func (h *ConsultHandler) orderDetail(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.OrderDetail(c.Request.Context(), uid, qInt64(c, "orderId"))
h.ok(c, data, err)
}
func (h *ConsultHandler) orderCancel(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
if err := h.Svc.CancelOrder(c.Request.Context(), uid, qInt64(c, "orderId")); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) orderDelete(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
if err := h.Svc.DeleteOrder(c.Request.Context(), uid, qInt64(c, "orderId")); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) payDemo(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.DemoPay(c.Request.Context(), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) payRefund(c *gin.Context) {
response.OK(c, true)
}
func (h *ConsultHandler) payNotify(c *gin.Context) {
raw, _ := c.GetRawData()
sn := extractXML(string(raw), "out_trade_no")
if sn != "" {
_ = h.Svc.MarkPaid(c.Request.Context(), sn)
}
c.String(http.StatusOK, "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>")
}
func (h *ConsultHandler) selfInfo(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.SelfInfo(c.Request.Context(), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) updateSelf(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var in struct {
NickName string `json:"nickName"`
AvatarUrl string `json:"avatarUrl"`
StayPeriod string `json:"stayPeriod"`
}
_ = c.ShouldBindJSON(&in)
if err := h.Svc.UpdateSelf(c.Request.Context(), uid, in.NickName, in.AvatarUrl, in.StayPeriod); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) notRead(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.NotRead(c.Request.Context(), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) focusRead(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
_ = h.Svc.FocusToRead(c.Request.Context(), uid)
response.OK(c, true)
}
func (h *ConsultHandler) orderRead(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
_ = h.Svc.OrderToRead(c.Request.Context(), uid)
response.OK(c, true)
}
func (h *ConsultHandler) focusAll(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.FocusAll(c.Request.Context(), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) feedback(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
var in struct {
ContentText string `json:"contentText"`
}
_ = c.ShouldBindJSON(&in)
if err := h.Svc.Feedback(c.Request.Context(), uid, in.ContentText); err != nil {
response.Fail(c, http.StatusBadRequest, 40001, err.Error())
return
}
response.OK(c, true)
}
func (h *ConsultHandler) feedbackFlag(c *gin.Context) {
uid, ok := h.mustUser(c)
if !ok {
return
}
data, err := h.Svc.FeedbackFlag(c.Request.Context(), uid)
h.ok(c, data, err)
}
func (h *ConsultHandler) mustUser(c *gin.Context) (uuid.UUID, bool) {
uid, ok := middleware.UserIDFromContext(c)
if !ok || !h.Svc.Registered(c.Request.Context(), uid) {
response.Fail(c, http.StatusUnauthorized, 40112, "请先登录后再使用")
return uuid.Nil, false
}
return uid, true
}
func qInt(c *gin.Context, key string, def int) int {
s := c.Query(key)
if s == "" {
return def
}
n, err := strconv.Atoi(s)
if err != nil {
return def
}
return n
}
func qInt64(c *gin.Context, key string) int64 {
n, _ := strconv.ParseInt(c.Query(key), 10, 64)
return n
}
func extractXML(s, tag string) string {
open, close := "<"+tag+">", "</"+tag+">"
i := strings.Index(s, open)
if i < 0 {
open, close = "<"+tag+"><![CDATA[", "]]></"+tag+">"
i = strings.Index(s, open)
if i < 0 {
return ""
}
}
i += len(open)
j := strings.Index(s[i:], close)
if j < 0 {
return ""
}
return s[i : i+j]
}