| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 | package svcimport (	"fmt"	"net/http"	"golib/v3/features/mo"	"golib/v3/gio"	"golib/v3/gnet"	"golib/v3/infra/ii")const (	// Method Post	cmdInsertOne  = "insertOne"	cmdInsertMany = "insertMany"	cmdUpdateOne  = "updateOne"	cmdUpdateMany = "updateMany"	cmdUpdateById = "updateById"	cmdFindOne    = "findOne"	cmdFind       = "find"	cmdCount      = "count"	cmdDeleteOne  = "deleteOne"	cmdDeleteMany = "deleteMany")var (	actionMap = map[string]struct{}{		cmdInsertOne:  {},		cmdInsertMany: {},		cmdUpdateOne:  {},		cmdUpdateMany: {},		cmdUpdateById: {},		cmdDeleteOne:  {},		cmdDeleteMany: {},		cmdFind:       {},		cmdFindOne:    {},		cmdCount:      {},	})const (	HTTPMaxRequestSize = 4096)// action: insertOne/insertMany/updateOne/updateMany/deleteOne/deleteMany/find/findOne// Request: {"action":"insert", "itemName":"test.test", "fields": {"name":"xiaoming","age":3.1415}}// Response: {"action":"insert", "itemName": "test.test", "ret":"success", "result":"","fields":{"name":"required"}}type httpHandleBody struct {	CMD      string  `json:"cmd"` // CMD 本次请求需要执行的命令	ItemName ii.Name `json:"itemName"`	Data     any     `json:"data"` // Data 数据类型根据 action 变化	ExtData  any     `json:"extData"`}type HttpHandler struct {	Items       ii.Items	User        ii.User	RequestSize int64}func (f *HttpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {	cmd, itemName, err := splitPATH(r.URL.Path, "svc")	if err != nil {		http.Error(w, err.Error(), http.StatusForbidden)		return	}	if _, ok := actionMap[cmd]; !ok {		http.Error(w, "unknown cmd", http.StatusNotFound)		return	}	if _, ok := f.Items.Has(itemName); !ok {		http.Error(w, ErrItemNotfound.Error(), http.StatusNotFound)		return	}	if f.RequestSize <= 0 {		f.RequestSize = HTTPMaxRequestSize	}	b, err := gio.ReadLimit(r.Body, f.RequestSize)	if err != nil {		http.Error(w, err.Error(), http.StatusBadRequest)		return	}	var hrb httpHandleBody	if err = mo.UnmarshalExtJSON(b, true, true); err != nil {		http.Error(w, err.Error(), http.StatusBadRequest)		return	}	hrb.ItemName = itemName	hrb.CMD = cmd	switch hrb.CMD {	case cmdInsertOne:		f.handleInsertOne(w, &hrb)	case cmdInsertMany:		f.handleInsertMany(w, &hrb)	case cmdUpdateOne:		f.handleUpdateOne(w, &hrb)	case cmdUpdateMany:		f.handleUpdateMany(w, &hrb)	case cmdUpdateById:		f.handleUpdateByID(w, &hrb)	case cmdDeleteOne:		f.handleDeleteOne(w, &hrb)	case cmdDeleteMany:		f.handleDeleteMany(w, &hrb)	case cmdFindOne:		f.handleFindOne(w, &hrb)	case cmdFind:		f.handleFind(w, &hrb)	case cmdCount:		f.handleCount(w, &hrb)	}}func (f *HttpHandler) handleFind(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	rows, err := With(f.User).Find(hrb.ItemName, filter)	if err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     rows,	}	f.respJson(w, resp)}func (f *HttpHandler) handleFindOne(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	row, err := With(f.User).FindOne(hrb.ItemName, filter)	if err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     row,	}	f.respJson(w, resp)}func (f *HttpHandler) handleInsertOne(w http.ResponseWriter, hrb *httpHandleBody) {	data, ok := hrb.Data.(map[string]any)	if !ok {		f.respJsonErr(w, ErrDataError, http.StatusBadRequest)		return	}	oid, err := With(f.User).InsertOne(hrb.ItemName, data)	if err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     oid,	}	f.respJson(w, resp)}func (f *HttpHandler) handleInsertMany(w http.ResponseWriter, hrb *httpHandleBody) {	data, ok := hrb.Data.([]any)	if !ok {		f.respJsonErr(w, ErrDataError, http.StatusBadRequest)		return	}	oidList, err := With(f.User).InsertMany(hrb.ItemName, data)	if err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     oidList,	}	f.respJson(w, resp)}func (f *HttpHandler) handleUpdateOne(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	update, err := f.handleUpdateExtData(hrb)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	if err = With(f.User).UpdateOne(hrb.ItemName, filter, update); err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     nil,	}	f.respJson(w, resp)}func (f *HttpHandler) handleUpdateByID(w http.ResponseWriter, hrb *httpHandleBody) {	idStr, ok := hrb.Data.(string)	if !ok {		f.respJsonErr(w, ErrDataError, http.StatusBadRequest)		return	}	oid, err := mo.ID.From(idStr)	if err != nil {		f.respJsonErr(w, ErrDataError, http.StatusBadRequest)		return	}	update, err := f.handleUpdateExtData(hrb)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	if err = With(f.User).UpdateByID(hrb.ItemName, oid, update); err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     nil,	}	f.respJson(w, resp)}func (f *HttpHandler) handleUpdateMany(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	update, err := f.handleUpdateExtData(hrb)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	if err = With(f.User).UpdateMany(hrb.ItemName, filter, update); err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     nil,	}	f.respJson(w, resp)}func (f *HttpHandler) handleCount(w http.ResponseWriter, hrb *httpHandleBody) {	var (		total int64		err   error	)	if hrb.Data == nil || hrb.Data == "" {		total, err = With(f.User).EstimatedDocumentCount(hrb.ItemName)	} else {		filter, err := f.handleFilterData(hrb.Data)		if err != nil {			f.respJsonErr(w, err, http.StatusBadRequest)			return		}		total, err = With(f.User).CountDocuments(hrb.ItemName, filter)	}	if err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     total,	}	f.respJson(w, resp)}func (f *HttpHandler) handleDeleteOne(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	if err = With(f.User).DeleteOne(hrb.ItemName, filter); err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     nil,	}	f.respJson(w, resp)}func (f *HttpHandler) handleDeleteMany(w http.ResponseWriter, hrb *httpHandleBody) {	filter, err := f.handleFilterData(hrb.Data)	if err != nil {		f.respJsonErr(w, err, http.StatusBadRequest)		return	}	if err = With(f.User).DeleteMany(hrb.ItemName, filter); err != nil {		f.respJsonErr(w, err, http.StatusInternalServerError)		return	}	resp := &httpHandleBody{		CMD:      hrb.CMD,		ItemName: hrb.ItemName,		Data:     nil,	}	f.respJson(w, resp)}func (f *HttpHandler) handleUpdateExtData(hrb *httpHandleBody) (mo.Filter, error) {	switch v := hrb.ExtData.(type) {	case map[string]any:		set, err := mo.ToD(v)		if err != nil {			return nil, err		}		return &mo.Updater{Setter: set}, nil	default:		return nil, fmt.Errorf("unsupport data type")	}}func (f *HttpHandler) handleFilterData(data any) (mo.Filter, error) {	b, err := mo.MarshalExtJSON(data, true, true)	if err != nil {		return nil, err	}	var filter mo.D	if err = mo.UnmarshalExtJSON(b, true, &filter); err != nil {		return nil, err	}	return &mo.Matcher{Filter: filter}, nil}func (f *HttpHandler) respJson(w http.ResponseWriter, v any) {	p, err := mo.MarshalExtJSON(v, true, true)	if err != nil {		http.Error(w, err.Error(), http.StatusInternalServerError)		return	}	w.Header().Set("Content-Type", gnet.HTTPContentTypeJson)	_, _ = w.Write(p)}func (f *HttpHandler) respJsonErr(w http.ResponseWriter, err error, code int) {	w.Header().Set("Content-Type", gnet.HTTPContentTypeJson)	w.WriteHeader(code)	_, _ = w.Write([]byte(fmt.Sprintf(`{"result":"%s"}`, err)))}
 |