feat(1.0):增加客服管理

This commit is contained in:
summer 2025-10-17 14:53:02 +08:00
parent a061e39dc7
commit 5af6831bb7
13 changed files with 1508 additions and 1 deletions

View File

@ -656,6 +656,160 @@ const docTemplate = `{
}
}
},
"/admin/create": {
"post": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "新增客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "新增客服",
"parameters": [
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.createAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.createAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/delete": {
"post": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "删除客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "删除客服",
"parameters": [
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.deleteAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.deleteAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/list": {
"get": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "客服列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "客服列表",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "query"
},
{
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "当前页码",
"name": "page",
"in": "query",
"required": true
},
{
"type": "integer",
"default": 20,
"description": "每页返回的数据量,最多 100 条",
"name": "page_size",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.listResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/login": {
"post": {
"description": "管理员登录",
@ -845,6 +999,58 @@ const docTemplate = `{
}
}
},
"/admin/{id}": {
"put": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "编辑客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "编辑客服",
"parameters": [
{
"type": "string",
"description": "编号ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.modifyAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.modifyAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/app/messages": {
"get": {
"description": "获取消息日志",
@ -988,6 +1194,122 @@ const docTemplate = `{
}
},
"definitions": {
"admin.createAdminRequest": {
"type": "object",
"required": [
"nickname",
"password",
"username"
],
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"password": {
"description": "密码",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.createAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"admin.deleteAdminRequest": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"description": "编号(多个用,分割)",
"type": "string"
}
}
},
"admin.deleteAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"admin.listData": {
"type": "object",
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"created_at": {
"description": "创建时间",
"type": "string"
},
"id": {
"description": "编号",
"type": "integer"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"updated_at": {
"description": "更新时间",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.listResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/admin.listData"
}
},
"page": {
"description": "当前页码",
"type": "integer"
},
"page_size": {
"description": "每页返回的数据量",
"type": "integer"
},
"total": {
"description": "符合查询条件的总记录数",
"type": "integer"
}
}
},
"admin.loginRequest": {
"type": "object",
"required": [
@ -1018,6 +1340,45 @@ const docTemplate = `{
}
}
},
"admin.modifyAdminRequest": {
"type": "object",
"required": [
"nickname",
"password",
"username"
],
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"password": {
"description": "密码",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.modifyAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"app.adminSendMessageRequest": {
"type": "object",
"required": [

View File

@ -648,6 +648,160 @@
}
}
},
"/admin/create": {
"post": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "新增客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "新增客服",
"parameters": [
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.createAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.createAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/delete": {
"post": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "删除客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "删除客服",
"parameters": [
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.deleteAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.deleteAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/list": {
"get": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "客服列表",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "客服列表",
"parameters": [
{
"type": "string",
"description": "用户名",
"name": "username",
"in": "query"
},
{
"type": "string",
"description": "昵称",
"name": "nickname",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "当前页码",
"name": "page",
"in": "query",
"required": true
},
{
"type": "integer",
"default": 20,
"description": "每页返回的数据量,最多 100 条",
"name": "page_size",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.listResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/admin/login": {
"post": {
"description": "管理员登录",
@ -837,6 +991,58 @@
}
}
},
"/admin/{id}": {
"put": {
"security": [
{
"LoginVerifyToken": []
}
],
"description": "编辑客服",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"管理端.客服管理"
],
"summary": "编辑客服",
"parameters": [
{
"type": "string",
"description": "编号ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "请求参数",
"name": "RequestBody",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/admin.modifyAdminRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/admin.modifyAdminResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/code.Failure"
}
}
}
}
},
"/app/messages": {
"get": {
"description": "获取消息日志",
@ -980,6 +1186,122 @@
}
},
"definitions": {
"admin.createAdminRequest": {
"type": "object",
"required": [
"nickname",
"password",
"username"
],
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"password": {
"description": "密码",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.createAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"admin.deleteAdminRequest": {
"type": "object",
"required": [
"ids"
],
"properties": {
"ids": {
"description": "编号(多个用,分割)",
"type": "string"
}
}
},
"admin.deleteAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"admin.listData": {
"type": "object",
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"created_at": {
"description": "创建时间",
"type": "string"
},
"id": {
"description": "编号",
"type": "integer"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"updated_at": {
"description": "更新时间",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.listResponse": {
"type": "object",
"properties": {
"list": {
"type": "array",
"items": {
"$ref": "#/definitions/admin.listData"
}
},
"page": {
"description": "当前页码",
"type": "integer"
},
"page_size": {
"description": "每页返回的数据量",
"type": "integer"
},
"total": {
"description": "符合查询条件的总记录数",
"type": "integer"
}
}
},
"admin.loginRequest": {
"type": "object",
"required": [
@ -1010,6 +1332,45 @@
}
}
},
"admin.modifyAdminRequest": {
"type": "object",
"required": [
"nickname",
"password",
"username"
],
"properties": {
"avatar": {
"description": "头像",
"type": "string"
},
"mobile": {
"description": "手机号",
"type": "string"
},
"nickname": {
"description": "昵称",
"type": "string"
},
"password": {
"description": "密码",
"type": "string"
},
"username": {
"description": "用户名",
"type": "string"
}
}
},
"admin.modifyAdminResponse": {
"type": "object",
"properties": {
"message": {
"description": "提示信息",
"type": "string"
}
}
},
"app.adminSendMessageRequest": {
"type": "object",
"required": [

View File

@ -1,5 +1,87 @@
basePath: /
definitions:
admin.createAdminRequest:
properties:
avatar:
description: 头像
type: string
mobile:
description: 手机号
type: string
nickname:
description: 昵称
type: string
password:
description: 密码
type: string
username:
description: 用户名
type: string
required:
- nickname
- password
- username
type: object
admin.createAdminResponse:
properties:
message:
description: 提示信息
type: string
type: object
admin.deleteAdminRequest:
properties:
ids:
description: 编号(多个用,分割)
type: string
required:
- ids
type: object
admin.deleteAdminResponse:
properties:
message:
description: 提示信息
type: string
type: object
admin.listData:
properties:
avatar:
description: 头像
type: string
created_at:
description: 创建时间
type: string
id:
description: 编号
type: integer
mobile:
description: 手机号
type: string
nickname:
description: 昵称
type: string
updated_at:
description: 更新时间
type: string
username:
description: 用户名
type: string
type: object
admin.listResponse:
properties:
list:
items:
$ref: '#/definitions/admin.listData'
type: array
page:
description: 当前页码
type: integer
page_size:
description: 每页返回的数据量
type: integer
total:
description: 符合查询条件的总记录数
type: integer
type: object
admin.loginRequest:
properties:
password:
@ -21,6 +103,34 @@ definitions:
description: 登录成功后颁发的 Token
type: string
type: object
admin.modifyAdminRequest:
properties:
avatar:
description: 头像
type: string
mobile:
description: 手机号
type: string
nickname:
description: 昵称
type: string
password:
description: 密码
type: string
username:
description: 用户名
type: string
required:
- nickname
- password
- username
type: object
admin.modifyAdminResponse:
properties:
message:
description: 提示信息
type: string
type: object
app.adminSendMessageRequest:
properties:
app_id:
@ -516,6 +626,39 @@ info:
title: mini-chat 接口文档
version: v0.0.1
paths:
/admin/{id}:
put:
consumes:
- application/json
description: 编辑客服
parameters:
- description: 编号ID
in: path
name: id
required: true
type: string
- description: 请求参数
in: body
name: RequestBody
required: true
schema:
$ref: '#/definitions/admin.modifyAdminRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/admin.modifyAdminResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
security:
- LoginVerifyToken: []
summary: 编辑客服
tags:
- 管理端.客服管理
/admin/app/{id}:
put:
consumes:
@ -928,6 +1071,104 @@ paths:
summary: 小程序列表
tags:
- 管理端.小程序
/admin/create:
post:
consumes:
- application/json
description: 新增客服
parameters:
- description: 请求参数
in: body
name: RequestBody
required: true
schema:
$ref: '#/definitions/admin.createAdminRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/admin.createAdminResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
security:
- LoginVerifyToken: []
summary: 新增客服
tags:
- 管理端.客服管理
/admin/delete:
post:
consumes:
- application/json
description: 删除客服
parameters:
- description: 请求参数
in: body
name: RequestBody
required: true
schema:
$ref: '#/definitions/admin.deleteAdminRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/admin.deleteAdminResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
security:
- LoginVerifyToken: []
summary: 删除客服
tags:
- 管理端.客服管理
/admin/list:
get:
consumes:
- application/json
description: 客服列表
parameters:
- description: 用户名
in: query
name: username
type: string
- description: 昵称
in: query
name: nickname
type: string
- default: 1
description: 当前页码
in: query
name: page
required: true
type: integer
- default: 20
description: 每页返回的数据量,最多 100 条
in: query
name: page_size
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/admin.listResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/code.Failure'
security:
- LoginVerifyToken: []
summary: 客服列表
tags:
- 管理端.客服管理
/admin/login:
post:
consumes:

View File

@ -0,0 +1,115 @@
package admin
import (
"fmt"
"net/http"
"time"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/utils"
"mini-chat/internal/pkg/validation"
"mini-chat/internal/repository/mysql/model"
"gorm.io/gorm"
)
type createAdminRequest struct {
UserName string `json:"username" binding:"required"` // 用户名
NickName string `json:"nickname" binding:"required"` // 昵称
Mobile string `json:"mobile"` // 手机号
Password string `json:"password" binding:"required"` // 密码
Avatar string `json:"avatar"` // 头像
}
type createAdminResponse struct {
Message string `json:"message"` // 提示信息
}
// CreateAdmin 新增客服
// @Summary 新增客服
// @Description 新增客服
// @Tags 管理端.客服管理
// @Accept json
// @Produce json
// @Param RequestBody body createAdminRequest true "请求参数"
// @Success 200 {object} createAdminResponse
// @Failure 400 {object} code.Failure
// @Router /admin/create [post]
// @Security LoginVerifyToken
func (h *handler) CreateAdmin() core.HandlerFunc {
return func(ctx core.Context) {
req := new(createAdminRequest)
res := new(createAdminResponse)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)),
)
return
}
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.CreateAdminError,
fmt.Sprintf("%s: %s", code.Text(code.CreateAdminError), "禁止操作")),
)
return
}
info, err := h.readDB.Admin.WithContext(ctx.RequestContext()).
Where(h.readDB.Admin.Username.Eq(req.UserName)).
First()
if err != nil && err != gorm.ErrRecordNotFound {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.CreateAdminError,
fmt.Sprintf("%s: %s", code.Text(code.CreateAdminError), err.Error())),
)
return
}
if info != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.CreateAdminError,
fmt.Sprintf("%s: %s", code.Text(code.CreateAdminError), "该账号已存在")),
)
return
}
hashedPassword, err := utils.GenerateAdminHashedPassword(req.Password)
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.CreateAdminError,
fmt.Sprintf("%s: %s", code.Text(code.CreateAdminError), err.Error())),
)
return
}
Admin := new(model.Admin)
Admin.Username = req.UserName
Admin.Nickname = req.NickName
Admin.Mobile = req.Mobile
Admin.Password = hashedPassword
Admin.Avatar = req.Avatar
Admin.LoginStatus = 1
Admin.IsSuper = 0
Admin.CreatedUser = ctx.SessionUserInfo().UserName
Admin.CreatedAt = time.Now()
if err := h.writeDB.Admin.WithContext(ctx.RequestContext()).Create(Admin); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.CreateAdminError,
fmt.Sprintf("%s: %s", code.Text(code.CreateAdminError), err.Error())),
)
return
}
res.Message = "操作成功"
ctx.Payload(res)
}
}

View File

@ -0,0 +1,109 @@
package admin
import (
"fmt"
"net/http"
"strconv"
"strings"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/validation"
"mini-chat/internal/repository/mysql/model"
)
type deleteAdminRequest struct {
Ids string `json:"ids" binding:"required"` // 编号(多个用,分割)
}
type deleteAdminResponse struct {
Message string `json:"message"` // 提示信息
}
// DeleteAdmin 删除客服
// @Summary 删除客服
// @Description 删除客服
// @Tags 管理端.客服管理
// @Accept json
// @Produce json
// @Param RequestBody body deleteAdminRequest true "请求参数"
// @Success 200 {object} deleteAdminResponse
// @Failure 400 {object} code.Failure
// @Router /admin/delete [post]
// @Security LoginVerifyToken
func (h *handler) DeleteAdmin() core.HandlerFunc {
return func(ctx core.Context) {
req := new(deleteAdminRequest)
res := new(deleteAdminResponse)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)),
)
return
}
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.DeleteAdminError,
fmt.Sprintf("%s: %s", code.Text(code.DeleteAdminError), "禁止操作")),
)
return
}
idList := strings.Split(req.Ids, ",")
if len(idList) == 0 || (len(idList) == 1 && idList[0] == "") {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
"编号不能为空"),
)
return
}
var ids []int32
for _, strID := range idList {
if strID == "" {
continue
}
id, err := strconv.Atoi(strID)
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
fmt.Sprintf("无效的编号: %s", strID)),
)
return
}
ids = append(ids, int32(id))
}
if len(ids) == 0 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
"编号不能为空"),
)
return
}
if _, err := h.writeDB.Admin.WithContext(ctx.RequestContext()).
Where(h.writeDB.Admin.IsSuper.Eq(0)).
Where(h.writeDB.Admin.ID.In(ids...)).
Delete(&model.Admin{}); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.DeleteAdminError,
fmt.Sprintf("%s: %s", code.Text(code.DeleteAdminError), err.Error())),
)
return
}
res.Message = "操作成功"
ctx.Payload(res)
}
}

148
internal/api/admin/admin_list.go Executable file
View File

@ -0,0 +1,148 @@
package admin
import (
"fmt"
"net/http"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/timeutil"
"mini-chat/internal/pkg/validation"
"gorm.io/gorm"
)
type listRequest struct {
Username string `form:"username"` // 用户名
Nickname string `form:"nickname"` // 昵称
Page int `form:"page"` // 当前页码,默认为第一页
PageSize int `form:"page_size"` // 每页返回的数据量
}
type listData struct {
ID int32 `json:"id"` // 编号
UserName string `json:"username"` // 用户名
NickName string `json:"nickname"` // 昵称
Mobile string `json:"mobile"` // 手机号
Avatar string `json:"avatar"` // 头像
CreatedAt string `json:"created_at"` // 创建时间
UpdatedAt string `json:"updated_at"` // 更新时间
}
type listResponse struct {
Page int `json:"page"` // 当前页码
PageSize int `json:"page_size"` // 每页返回的数据量
Total int64 `json:"total"` // 符合查询条件的总记录数
List []listData `json:"list"`
}
// PageList 客服列表
// @Summary 客服列表
// @Description 客服列表
// @Tags 管理端.客服管理
// @Accept json
// @Produce json
// @Param username query string false "用户名"
// @Param nickname query string false "昵称"
// @Param page query int true "当前页码" default(1)
// @Param page_size query int true "每页返回的数据量,最多 100 条" default(20)
// @Success 200 {object} listResponse
// @Failure 400 {object} code.Failure
// @Router /admin/list [get]
// @Security LoginVerifyToken
func (h *handler) PageList() core.HandlerFunc {
return func(ctx core.Context) {
req := new(listRequest)
res := new(listResponse)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)),
)
return
}
if req.Page == 0 {
req.Page = 1
}
if req.PageSize == 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListAdminError,
fmt.Sprintf("%s: 一次最多只能查询 100 条", code.Text(code.ListAdminError)),
))
return
}
query := h.readDB.Admin.WithContext(ctx.RequestContext())
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ListAdminError), "禁止操作")),
)
return
}
if req.Username != "" {
query = query.Where(h.readDB.Admin.Username.Like(fmt.Sprintf("%%%s%%", req.Username)))
}
if req.Nickname != "" {
query = query.Where(h.readDB.Admin.Nickname.Like(fmt.Sprintf("%%%s%%", req.Nickname)))
}
listQueryDB := query.Session(&gorm.Session{})
countQueryDB := query.Session(&gorm.Session{})
resultData, err := listQueryDB.
Order(h.readDB.Admin.ID.Desc()).
Limit(req.PageSize).
Offset((req.Page - 1) * req.PageSize).Find()
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListAdminError,
fmt.Sprintf("%s%s", code.Text(code.ListAdminError), err.Error())),
)
return
}
count, err := countQueryDB.Count()
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ListAdminError,
fmt.Sprintf("%s%s", code.Text(code.ListAdminError), err.Error())),
)
return
}
res.Page = req.Page
res.PageSize = req.PageSize
res.Total = count
res.List = make([]listData, len(resultData))
for k, v := range resultData {
res.List[k] = listData{
ID: v.ID,
UserName: v.Username,
NickName: v.Nickname,
Mobile: v.Mobile,
Avatar: v.Avatar,
CreatedAt: timeutil.FriendlyTime(v.CreatedAt),
UpdatedAt: timeutil.FriendlyTime(v.CreatedAt),
}
}
ctx.Payload(res)
}
}

View File

@ -0,0 +1,154 @@
package admin
import (
"fmt"
"net/http"
"strconv"
"time"
"mini-chat/internal/code"
"mini-chat/internal/pkg/core"
"mini-chat/internal/pkg/utils"
"mini-chat/internal/pkg/validation"
"gorm.io/gorm"
)
type modifyAdminRequest struct {
UserName string `json:"username" binding:"required"` // 用户名
NickName string `json:"nickname" binding:"required"` // 昵称
Mobile string `json:"mobile"` // 手机号
Password string `json:"password" binding:"required"` // 密码
Avatar string `json:"avatar"` // 头像
}
type modifyAdminResponse struct {
Message string `json:"message"` // 提示信息
}
// ModifyAdmin 编辑客服
// @Summary 编辑客服
// @Description 编辑客服
// @Tags 管理端.客服管理
// @Accept json
// @Produce json
// @Param id path string true "编号ID"
// @Param RequestBody body modifyAdminRequest true "请求参数"
// @Success 200 {object} modifyAdminResponse
// @Failure 400 {object} code.Failure
// @Router /admin/{id} [put]
// @Security LoginVerifyToken
func (h *handler) ModifyAdmin() core.HandlerFunc {
return func(ctx core.Context) {
req := new(modifyAdminRequest)
res := new(modifyAdminResponse)
if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
validation.Error(err)),
)
return
}
if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), "禁止操作")),
)
return
}
if req.UserName == "" && req.NickName == "" && req.Password == "" {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
"用户名、昵称、密码为必填"),
)
return
}
id, err := strconv.Atoi(ctx.Param("id"))
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ParamBindError,
"未传递编号ID"),
)
return
}
checkIdInfo, err := h.readDB.Admin.WithContext(ctx.RequestContext()).
Where(h.readDB.Admin.ID.Eq(int32(id))).
First()
if err != nil && err != gorm.ErrRecordNotFound {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), err.Error())),
)
return
}
if checkIdInfo == nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), "该账号不存在")),
)
return
}
checkUserNameInfo, err := h.readDB.Admin.WithContext(ctx.RequestContext()).
Where(h.readDB.Admin.ID.Neq(int32(id))).
Where(h.readDB.Admin.Username.Eq(req.UserName)).
First()
if err != nil && err != gorm.ErrRecordNotFound {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), err.Error())),
)
return
}
if checkUserNameInfo != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), "该账号已存在")),
)
return
}
hashedPassword, err := utils.GenerateAdminHashedPassword(req.Password)
if err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s: %s", code.Text(code.ModifyAdminError), err.Error())),
)
return
}
checkIdInfo.Username = req.UserName
checkIdInfo.Nickname = req.NickName
checkIdInfo.Mobile = req.Mobile
checkIdInfo.Password = hashedPassword
checkIdInfo.Avatar = req.Avatar
checkIdInfo.UpdatedUser = ctx.SessionUserInfo().UserName
checkIdInfo.UpdatedAt = time.Now()
if err := h.writeDB.Admin.WithContext(ctx.RequestContext()).Save(checkIdInfo); err != nil {
ctx.AbortWithError(core.Error(
http.StatusBadRequest,
code.ModifyAdminError,
fmt.Sprintf("%s%s", code.Text(code.ModifyAdminError), err.Error())),
)
return
}
res.Message = "操作成功"
ctx.Payload(res)
}
}

View File

@ -28,6 +28,10 @@ const (
ModifyAppError = 20204
CreateAppUserError = 20205
ListAppUserError = 20206
CreateAdminError = 20207
ListAdminError = 20208
ModifyAdminError = 20209
DeleteAdminError = 20210
CreateKeywordError = 20301
ListKeywordError = 20302

View File

@ -14,6 +14,10 @@ var zhCNText = map[int]string{
ModifyAppError: "修改小程序失败",
CreateAppUserError: "创建用户失败",
ListAppUserError: "获取用户列表失败",
CreateAdminError: "创建客服失败",
ListAdminError: "获取客服列表失败",
ModifyAdminError: "修改客服失败",
DeleteAdminError: "删除客服失败",
CreateKeywordError: "创建关键字失败",
DeleteKeywordError: "删除关键字失败",

View File

@ -30,6 +30,7 @@ func newAdmin(db *gorm.DB, opts ...gen.DOOption) admin {
_admin.ID = field.NewInt32(tableName, "id")
_admin.Username = field.NewString(tableName, "username")
_admin.Nickname = field.NewString(tableName, "nickname")
_admin.Avatar = field.NewString(tableName, "avatar")
_admin.Mobile = field.NewString(tableName, "mobile")
_admin.Password = field.NewString(tableName, "password")
_admin.IsSuper = field.NewInt32(tableName, "is_super")
@ -55,6 +56,7 @@ type admin struct {
ID field.Int32 // 主键
Username field.String // 用户名
Nickname field.String // 昵称
Avatar field.String // 头像
Mobile field.String // 手机号
Password field.String // 密码
IsSuper field.Int32 // 是否为超管(1:是 0:否)
@ -85,6 +87,7 @@ func (a *admin) updateTableName(table string) *admin {
a.ID = field.NewInt32(table, "id")
a.Username = field.NewString(table, "username")
a.Nickname = field.NewString(table, "nickname")
a.Avatar = field.NewString(table, "avatar")
a.Mobile = field.NewString(table, "mobile")
a.Password = field.NewString(table, "password")
a.IsSuper = field.NewInt32(table, "is_super")
@ -112,10 +115,11 @@ func (a *admin) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
}
func (a *admin) fillFieldMap() {
a.fieldMap = make(map[string]field.Expr, 14)
a.fieldMap = make(map[string]field.Expr, 15)
a.fieldMap["id"] = a.ID
a.fieldMap["username"] = a.Username
a.fieldMap["nickname"] = a.Nickname
a.fieldMap["avatar"] = a.Avatar
a.fieldMap["mobile"] = a.Mobile
a.fieldMap["password"] = a.Password
a.fieldMap["is_super"] = a.IsSuper

View File

@ -15,6 +15,7 @@ type Admin struct {
ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:主键" json:"id"` // 主键
Username string `gorm:"column:username;not null;comment:用户名" json:"username"` // 用户名
Nickname string `gorm:"column:nickname;not null;comment:昵称" json:"nickname"` // 昵称
Avatar string `gorm:"column:avatar;not null;comment:头像" json:"avatar"` // 头像
Mobile string `gorm:"column:mobile;not null;comment:手机号" json:"mobile"` // 手机号
Password string `gorm:"column:password;not null;comment:密码" json:"password"` // 密码
IsSuper int32 `gorm:"column:is_super;not null;comment:是否为超管(1:是 0:否)" json:"is_super"` // 是否为超管(1:是 0:否)

View File

@ -74,6 +74,11 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co
adminAuthApiRouter.PUT("/app/:id", appHandler.ModifyApp()) // 修改小程序
adminAuthApiRouter.GET("/apps", appHandler.PageList()) // 小程序列表
adminAuthApiRouter.POST("/create", adminHandler.CreateAdmin()) // 新增客服
adminAuthApiRouter.POST("/delete", adminHandler.DeleteAdmin()) // 删除客服
adminAuthApiRouter.PUT("/:id", adminHandler.ModifyAdmin()) // 编辑客服
adminAuthApiRouter.GET("/list", adminHandler.PageList()) // 客服列表
adminAuthApiRouter.POST("/app/keyword", keywordHandler.CreateKeyword()) // 添加意图关键字
adminAuthApiRouter.PUT("/app/keyword/:id", keywordHandler.ModifyKeyword()) // 修改意图关键字
adminAuthApiRouter.DELETE("/app/keyword/:id", keywordHandler.DeleteKeyword()) // 删除意图关键字